From 96eb4e71965ef75a45fb14d7c5119a747cec0a2d Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Mon, 23 Jun 2025 21:31:54 -0300 Subject: [PATCH 01/30] TAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less modeTAP5-2810: first pass at Require.js-less mode modified: tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java --- .../modules/BeanValidatorModule.java | 27 ++++++---- .../base/AbstractComponentEventLink.java | 8 +-- .../tapestry5/corelib/base/AbstractField.java | 6 ++- .../tapestry5/corelib/components/Alerts.java | 6 ++- .../tapestry5/corelib/components/DevTool.java | 14 +++++- .../tapestry5/corelib/components/Errors.java | 5 +- .../tapestry5/corelib/components/Form.java | 6 ++- .../corelib/components/FormFragment.java | 8 ++- .../corelib/components/Graphviz.java | 6 ++- .../tapestry5/corelib/components/Grid.java | 8 +-- .../tapestry5/corelib/components/Label.java | 6 ++- .../corelib/components/LocalDate.java | 13 +++-- .../tapestry5/corelib/components/Palette.java | 24 +++++++-- .../components/ProgressiveDisplay.java | 12 +++-- .../tapestry5/corelib/components/Select.java | 49 ++++++++++--------- .../corelib/mixins/Autocomplete.java | 28 ++++++++--- .../tapestry5/corelib/mixins/Confirm.java | 8 +-- .../corelib/mixins/TriggerFragment.java | 10 ++-- .../tapestry5/corelib/mixins/ZoneRefresh.java | 9 ++-- .../internal/alerts/AlertManagerImpl.java | 20 +++++++- .../services/ajax/JavaScriptSupportImpl.java | 19 +++++-- .../services/ajax/RequireJsModeHelper.java | 41 ++++++++++++++++ .../ajax/RequireJsModeHelperImpl.java | 49 +++++++++++++++++++ .../NumericTranslatorSupportImpl.java | 20 ++++---- .../tapestry5/modules/JavaScriptModule.java | 14 ++++-- .../javascript/JavaScriptSupport.java | 8 +++ .../validator/CheckboxValidator.java | 10 +++- .../org/apache/tapestry5/validator/Email.java | 9 +++- .../org/apache/tapestry5/validator/Max.java | 9 +++- .../apache/tapestry5/validator/MaxLength.java | 9 +++- .../org/apache/tapestry5/validator/Min.java | 10 +++- .../apache/tapestry5/validator/MinLength.java | 9 +++- .../apache/tapestry5/validator/Regexp.java | 9 +++- .../apache/tapestry5/validator/Required.java | 9 +++- .../ajax/JavaScriptSupportImplTest.groovy | 34 ++++++------- .../app1/pages/ModuleInitDemo.java | 10 ++-- .../app1/pages/MultiZoneUpdateDemo.java | 12 ++++- .../integration/app1/services/AppModule.java | 1 + .../tapestry5/upload/components/Upload.java | 18 ++++++- 39 files changed, 436 insertions(+), 137 deletions(-) create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelper.java create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelperImpl.java diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java index 2f93c789b5..d41a3d25e4 100644 --- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java +++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java @@ -14,6 +14,7 @@ package org.apache.tapestry5.beanvalidator.modules; import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.beanvalidator.*; import org.apache.tapestry5.commons.Configuration; import org.apache.tapestry5.commons.MappedConfiguration; @@ -21,6 +22,7 @@ import org.apache.tapestry5.internal.beanvalidator.*; import org.apache.tapestry5.ioc.ServiceBinder; import org.apache.tapestry5.ioc.annotations.Local; +import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.services.PropertyShadowBuilder; import org.apache.tapestry5.ioc.services.ThreadLocale; import org.apache.tapestry5.services.FieldValidatorDefaultSource; @@ -42,6 +44,7 @@ public class BeanValidatorModule { + private static final String VALIDATION_MODULE_NAME = "t5/core/validation"; private static final String MODULE_NAME = "t5/beanvalidator/beanvalidator-validation"; public static void bind(final ServiceBinder binder) @@ -92,14 +95,20 @@ public void configure(jakarta.validation.Configuration configuration) } public static void contributeClientConstraintDescriptorSource(final JavaScriptSupport javaScriptSupport, - final Configuration configuration) + final Configuration configuration, + final @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) boolean requireJsEnabled) { + + final Runnable importJs = requireJsEnabled ? + () -> javaScriptSupport.require(VALIDATION_MODULE_NAME) : + () -> javaScriptSupport.importEsModule(VALIDATION_MODULE_NAME); + configuration.add(new BaseCCD(Max.class, "value") { @Override public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require("t5/core/validation"); + importJs.run(); writer.attributes( "data-validate", true, "data-validate-max", attributes.get("value"), @@ -113,7 +122,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require("t5/core/validation"); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, "data-validate-min", attributes.get("value"), @@ -127,7 +136,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require("t5/core/validation"); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, "data-optionality", "required", @@ -140,7 +149,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require(MODULE_NAME); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, "data-optionality", "prohibited", @@ -153,7 +162,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require(MODULE_NAME); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, "data-validate-regexp", attributes.get("regexp"), @@ -166,7 +175,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require(MODULE_NAME); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, "data-range-message", message); @@ -192,7 +201,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require("t5/core/validation"); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, @@ -206,7 +215,7 @@ public void applyClientValidation(MarkupWriter writer, String message, Map attributes) { - javaScriptSupport.require("t5/core/validation"); + importJs.run(); writer.attributes( DataConstants.VALIDATION_ATTRIBUTE, true, diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractComponentEventLink.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractComponentEventLink.java index 64ac711afd..4e27a3f6b4 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractComponentEventLink.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractComponentEventLink.java @@ -20,8 +20,8 @@ import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.http.Link; import org.apache.tapestry5.http.services.Request; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; -import org.apache.tapestry5.services.javascript.JavaScriptSupport; /** * Base class for link-generating components that are based on a component event request. Such events have an event @@ -57,8 +57,8 @@ public abstract class AbstractComponentEventLink extends AbstractLink @Inject private Request request; - @Environmental - private JavaScriptSupport javaScriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; void beginRender(MarkupWriter writer) { @@ -72,7 +72,7 @@ void beginRender(MarkupWriter writer) if (async) { - javaScriptSupport.require("t5/core/zone"); + requireJsModeHelper.importModule("t5/core/zone"); writer.attributes("data-async-trigger", true); } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java index 654917abd5..22e991caec 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractField.java @@ -22,6 +22,7 @@ import org.apache.tapestry5.internal.BeanValidationContext; import org.apache.tapestry5.internal.InternalComponentResources; import org.apache.tapestry5.internal.services.FormControlNameManager; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.internal.util.InternalUtils; @@ -154,6 +155,9 @@ public String toString() @Environmental protected JavaScriptSupport javaScriptSupport; + + @Inject + private RequireJsModeHelper requireJsModeHelper; @Environmental protected ValidationTracker validationTracker; @@ -296,7 +300,7 @@ final void afterDecorator() if (error != null) { - javaScriptSupport.require("t5/core/fields").invoke("showValidationError").with(assignedClientId, error); + requireJsModeHelper.importModule("t5/core/fields").invoke("showValidationError").with(assignedClientId, error); } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Alerts.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Alerts.java index cf6e06400b..6893fc9127 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Alerts.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Alerts.java @@ -20,6 +20,7 @@ import org.apache.tapestry5.corelib.base.BaseClientElement; import org.apache.tapestry5.http.Link; import org.apache.tapestry5.http.services.Request; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.json.JSONObject; import org.apache.tapestry5.services.compatibility.DeprecationWarning; @@ -63,6 +64,9 @@ public class Alerts extends BaseClientElement @Inject private Request request; + + @Inject + private RequireJsModeHelper requireJsModeHelper; void onPageLoaded() { @@ -120,7 +124,7 @@ void addAlertsFromStorage() for (Alert alert : storage.getAlerts()) { - javaScriptSupport.require("t5/core/alert").with(alert.toJSON()); + requireJsModeHelper.importModule("t5/core/alert").with(alert.toJSON()); } storage.dismissNonPersistent(); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java index 3c6f9f614d..9c0ad42cc2 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java @@ -14,6 +14,7 @@ import org.apache.tapestry5.BindingConstants; import org.apache.tapestry5.ComponentResources; +import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.alerts.AlertManager; import org.apache.tapestry5.annotations.Component; import org.apache.tapestry5.annotations.Environmental; @@ -94,6 +95,10 @@ public class DevTool @Inject private ReloadHelper reloadHelper; + + @Inject + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) + private boolean requireJsEnabled; public String getZoneElement() { @@ -122,7 +127,14 @@ boolean beginRender() { if (enabled) { - javaScriptSupport.importStack("core").require("bootstrap/dropdown"); + if (requireJsEnabled) + { + javaScriptSupport.importStack("core").require("bootstrap/dropdown"); + } + else + { + javaScriptSupport.importEsModule("bootstrap/dropdown"); + } } return enabled; diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java index 7438dfd764..71c2cb6f20 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java @@ -24,6 +24,7 @@ import org.apache.tapestry5.annotations.Environmental; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.commons.util.CollectionFactory; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.services.ComponentOverride; @@ -79,7 +80,7 @@ public class Errors private String closeButtonCssClass; @Inject - private JavaScriptSupport javaScriptSupport; + private RequireJsModeHelper requireJsModeHelper; boolean beginRender(MarkupWriter writer) { @@ -146,7 +147,7 @@ boolean beginRender(MarkupWriter writer) * @see ComponentOverride */ protected void setUpJavaScript() { - javaScriptSupport.require("bootstrap/alert"); + requireJsModeHelper.importModule("bootstrap/alert"); } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java index 3d0fe289b6..5d8b8222c3 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java @@ -33,6 +33,7 @@ import org.apache.tapestry5.internal.InternalConstants; import org.apache.tapestry5.internal.services.FormControlNameManager; import org.apache.tapestry5.internal.services.HeartbeatImpl; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.internal.util.AutofocusValidationDecorator; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; @@ -238,6 +239,9 @@ public class Form implements ClientElement, FormValidationControl @Environmental private JavaScriptSupport javascriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; + @Inject private Request request; @@ -394,7 +398,7 @@ void beginRender(MarkupWriter writer) if (async) { - javascriptSupport.require("t5/core/zone"); + requireJsModeHelper.importModule("t5/core/zone"); writer.attributes("data-async-trigger", true); } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java index 972fde06df..cdd4de276b 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/FormFragment.java @@ -23,6 +23,7 @@ import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner; import org.apache.tapestry5.corelib.mixins.TriggerFragment; import org.apache.tapestry5.dom.Element; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.ClientDataEncoder; import org.apache.tapestry5.services.Environment; @@ -122,9 +123,12 @@ public class FormFragment implements ClientElement @Inject private Environment environment; - + @Environmental private JavaScriptSupport javascriptSupport; + + @Inject + private RequireJsModeHelper requireJsModeHelper; @Inject private ComponentResources resources; @@ -177,7 +181,7 @@ void beginRender(MarkupWriter writer) if (!alwaysSubmit) { - javascriptSupport.require("t5/core/form-fragment").invoke("hide").with(clientId); + requireJsModeHelper.importModule("t5/core/form-fragment").invoke("hide").with(clientId); } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java index 0f34cd3ec9..d735c60db5 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Graphviz.java @@ -21,6 +21,7 @@ import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.commons.Messages; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.ajax.AjaxResponseRenderer; import org.apache.tapestry5.services.javascript.JavaScriptSupport; @@ -58,6 +59,9 @@ public class Graphviz @Environmental private JavaScriptSupport javaScriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; + @Inject private AjaxResponseRenderer ajaxResponseRenderer; @@ -88,7 +92,7 @@ void setupRender(MarkupWriter writer) // TODO import https://cdn.jsdelivr.net/npm/@hpcc-js/wasm/dist/graphviz.js // if Require.js is disabled @hpcc-js/wasm - javaScriptSupport.require("t5/core/graphviz").with(cachedValue, id, showDownloadLink); + requireJsModeHelper.importModule("t5/core/graphviz").with(cachedValue, id, showDownloadLink); if (showDownloadLink) { diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java index 79e120b79b..bb5e5c3ee4 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Grid.java @@ -22,12 +22,12 @@ import org.apache.tapestry5.grid.*; import org.apache.tapestry5.internal.TapestryInternalUtils; import org.apache.tapestry5.internal.bindings.AbstractBinding; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.services.ComponentDefaultProvider; import org.apache.tapestry5.services.ComponentEventResultProcessor; import org.apache.tapestry5.services.FormSupport; -import org.apache.tapestry5.services.javascript.JavaScriptSupport; import java.io.IOException; import java.util.Collections; @@ -234,8 +234,8 @@ public class Grid implements GridModel, ClientElement @Inject private BeanModelSource modelSource; - @Environmental - private JavaScriptSupport javaScriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; @Component(parameters = {"index=inherit:columnIndex", "lean=inherit:lean", "overrides=overrides", "zone=zone"}) @@ -591,7 +591,7 @@ Object beginRender(MarkupWriter writer) if (inPlace && zone == null) { - javaScriptSupport.require("t5/core/zone"); + requireJsModeHelper.importModule("t5/core/zone"); writer.element("div", "data-container-type", "zone"); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java index c64853a386..529b027c53 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java @@ -19,6 +19,7 @@ import org.apache.tapestry5.annotations.SupportsInformalParameters; import org.apache.tapestry5.dom.Element; import org.apache.tapestry5.http.TapestryHttpSymbolConstants; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.internal.util.InternalUtils; @@ -67,6 +68,9 @@ public class Label @Inject private JavaScriptSupport javaScriptSupport; + + @Inject + private RequireJsModeHelper requireJsModeHelper; @Inject @Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE) @@ -120,7 +124,7 @@ private void updateAttributes() // TAP5-2500 String warningText = "The Label component " + resources.getCompleteId() + " is linked to a Field that failed to return a clientId. The 'for' attibute will not be rendered."; - javaScriptSupport.require("t5/core/console").invoke("warn").with(warningText); + requireJsModeHelper.importModule("t5/core/console").invoke("warn").with(warningText); } String id = clientId != null ? clientId : javaScriptSupport.allocateClientId(fieldId + "-label"); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/LocalDate.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/LocalDate.java index 797bc5fabd..f9f0f33b1e 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/LocalDate.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/LocalDate.java @@ -12,17 +12,16 @@ package org.apache.tapestry5.corelib.components; +import java.util.Date; + import org.apache.tapestry5.BindingConstants; import org.apache.tapestry5.ComponentResources; import org.apache.tapestry5.MarkupWriter; -import org.apache.tapestry5.annotations.Environmental; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.annotations.SupportsInformalParameters; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.DateUtilities; -import org.apache.tapestry5.services.javascript.JavaScriptSupport; - -import java.util.Date; /** * Used to present a date, formatted in the time zone of the client browser. @@ -62,8 +61,8 @@ public class LocalDate @Inject ComponentResources resources; - @Environmental - JavaScriptSupport javaScriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; @Inject DateUtilities dateUtilities; @@ -81,7 +80,7 @@ boolean beginRender(MarkupWriter writer) writer.end(); - javaScriptSupport.require("t5/core/localdate"); + requireJsModeHelper.importModule("t5/core/localdate"); } // Skip the body regardless. diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java index 69313c37bf..955a3840bc 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Palette.java @@ -12,19 +12,31 @@ package org.apache.tapestry5.corelib.components; -import org.apache.tapestry5.*; +import java.util.Collection; + +import org.apache.tapestry5.Asset; +import org.apache.tapestry5.Binding; +import org.apache.tapestry5.BindingConstants; +import org.apache.tapestry5.Block; +import org.apache.tapestry5.ComponentParameterConstants; +import org.apache.tapestry5.FieldValidator; +import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.Renderable; +import org.apache.tapestry5.SelectModel; +import org.apache.tapestry5.SymbolConstants; +import org.apache.tapestry5.ValidationException; +import org.apache.tapestry5.ValueEncoder; import org.apache.tapestry5.annotations.Import; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.corelib.base.AbstractField; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.internal.util.SelectModelRenderer; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.json.JSONArray; import org.apache.tapestry5.services.compatibility.DeprecationWarning; -import java.util.Collection; - /** * Multiple selection component. Generates a UI consisting of two <select> elements configured for multiple * selection; the one on the left is the list of "available" elements, the one on the right is "selected". Elements can @@ -160,7 +172,6 @@ public class Palette extends AbstractField * @since 5.2.0 */ @Parameter(defaultPrefix = BindingConstants.VALIDATE) - @SuppressWarnings("unchecked") private FieldValidator validate; @Inject @@ -169,6 +180,9 @@ public class Palette extends AbstractField @Inject private DeprecationWarning deprecationWarning; + + @Inject + private RequireJsModeHelper requireJsModeHelper; void pageLoaded() { deprecationWarning.ignoredComponentParameters(resources, "select", "moveUp", "moveDown", "deselect"); @@ -247,7 +261,7 @@ void beginRender() // The client side just need to know the id of the selected (right column) select; // it can take it from there. - javaScriptSupport.require("t5/core/palette").with(clientId); + requireJsModeHelper.importModule("t5/core/palette").with(clientId); } /** diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ProgressiveDisplay.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ProgressiveDisplay.java index 69da03e0f5..726f4cd6e6 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ProgressiveDisplay.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ProgressiveDisplay.java @@ -18,6 +18,7 @@ import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.annotations.SupportsInformalParameters; import org.apache.tapestry5.http.Link; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.compatibility.DeprecationWarning; import org.apache.tapestry5.services.javascript.JavaScriptSupport; @@ -63,9 +64,12 @@ public class ProgressiveDisplay @Inject private ComponentResources resources; - + @Environmental - private JavaScriptSupport jsSupport; + private JavaScriptSupport javaScriptSupport; + + @Inject + private RequireJsModeHelper requireJsModeHelper; @Environmental private TrackableComponentEventCallback eventCallback; @@ -91,7 +95,7 @@ void pageLoaded() { Block beginRender(MarkupWriter writer) { - String clientId = jsSupport.allocateClientId(resources); + String clientId = javaScriptSupport.allocateClientId(resources); String elementName = resources.getElementName("div"); writer.element(elementName, "id", clientId, "data-container-type", "zone"); @@ -99,7 +103,7 @@ Block beginRender(MarkupWriter writer) Link link = resources.createEventLink(EventConstants.ACTION, context); - jsSupport.require("t5/core/zone").invoke("deferredZoneUpdate").with(clientId, link.toURI()); + requireJsModeHelper.importModule("t5/core/zone").invoke("deferredZoneUpdate").with(clientId, link.toURI()); // Return the placeholder for the full content. That will render instead of the main body // of the component. diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java index 1c15c04386..76e61e874c 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Select.java @@ -12,9 +12,29 @@ package org.apache.tapestry5.corelib.components; -import org.apache.tapestry5.*; -import org.apache.tapestry5.annotations.*; -import org.apache.tapestry5.beanmodel.services.*; +import java.util.List; + +import org.apache.tapestry5.Binding; +import org.apache.tapestry5.BindingConstants; +import org.apache.tapestry5.ComponentParameterConstants; +import org.apache.tapestry5.EventConstants; +import org.apache.tapestry5.EventContext; +import org.apache.tapestry5.FieldValidationSupport; +import org.apache.tapestry5.FieldValidator; +import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.OptionGroupModel; +import org.apache.tapestry5.OptionModel; +import org.apache.tapestry5.SelectModel; +import org.apache.tapestry5.SelectModelVisitor; +import org.apache.tapestry5.ValidationException; +import org.apache.tapestry5.ValidationTracker; +import org.apache.tapestry5.ValueEncoder; +import org.apache.tapestry5.annotations.BeforeRenderTemplate; +import org.apache.tapestry5.annotations.Environmental; +import org.apache.tapestry5.annotations.Events; +import org.apache.tapestry5.annotations.Mixin; +import org.apache.tapestry5.annotations.Parameter; +import org.apache.tapestry5.annotations.RequestParameter; import org.apache.tapestry5.commons.Messages; import org.apache.tapestry5.commons.services.TypeCoercer; import org.apache.tapestry5.corelib.base.AbstractField; @@ -26,6 +46,7 @@ import org.apache.tapestry5.internal.AbstractEventContext; import org.apache.tapestry5.internal.InternalComponentResources; import org.apache.tapestry5.internal.TapestryInternalUtils; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.internal.util.CaptureResultCallback; import org.apache.tapestry5.internal.util.SelectModelRenderer; import org.apache.tapestry5.ioc.annotations.Inject; @@ -34,12 +55,8 @@ import org.apache.tapestry5.services.FormSupport; import org.apache.tapestry5.services.ValueEncoderFactory; import org.apache.tapestry5.services.ValueEncoderSource; -import org.apache.tapestry5.services.javascript.JavaScriptSupport; import org.apache.tapestry5.util.EnumSelectModel; -import java.util.Collections; -import java.util.List; - /** * Select an item from a list of values, using an [X]HTML <select> element on the client side. Any validation * decorations will go around the entire <select> element. @@ -180,12 +197,11 @@ protected boolean isOptionSelected(OptionModel optionModel, String clientValue) private FormSupport formSupport; @Inject - private JavaScriptSupport javascriptSupport; + private RequireJsModeHelper requireJsModeHelper; @Inject private TypeCoercer typeCoercer; - @SuppressWarnings("unused") @Mixin private RenderDisabled renderDisabled; @@ -196,8 +212,6 @@ private boolean isSelected(String clientValue) return TapestryInternalUtils.isEqual(clientValue, selectedClientValue); } - @SuppressWarnings( - {"unchecked"}) @Override protected void processSubmission(String controlName) { @@ -260,7 +274,7 @@ void beginRender(MarkupWriter writer) if (this.zone != null) { - javaScriptSupport.require("t5/core/select"); + requireJsModeHelper.importModule("t5/core/select"); Link link = resources.createEventLink(CHANGE_EVENT, context); @@ -391,17 +405,6 @@ private boolean findInOptions(List options, Object asSubmitted) return false; } - private static List orEmpty(List list) - { - if (list == null) - { - return Collections.emptyList(); - } - - return list; - } - - @SuppressWarnings("unchecked") ValueEncoder defaultEncoder() { return defaultProvider.defaultValueEncoder("value", resources); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java index 8d72839fd6..097fe79515 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Autocomplete.java @@ -12,11 +12,27 @@ package org.apache.tapestry5.corelib.mixins; -import org.apache.tapestry5.*; -import org.apache.tapestry5.annotations.*; +import java.util.Collections; +import java.util.List; + +import org.apache.tapestry5.BindingConstants; +import org.apache.tapestry5.ComponentEventCallback; +import org.apache.tapestry5.ComponentResources; +import org.apache.tapestry5.EventConstants; +import org.apache.tapestry5.EventContext; +import org.apache.tapestry5.Field; +import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.annotations.Environmental; +import org.apache.tapestry5.annotations.Events; +import org.apache.tapestry5.annotations.Import; +import org.apache.tapestry5.annotations.InjectContainer; +import org.apache.tapestry5.annotations.MixinAfter; +import org.apache.tapestry5.annotations.Parameter; +import org.apache.tapestry5.annotations.RequestParameter; import org.apache.tapestry5.commons.services.TypeCoercer; import org.apache.tapestry5.http.Link; import org.apache.tapestry5.internal.AbstractEventContext; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.internal.util.Holder; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.json.JSONArray; @@ -24,9 +40,6 @@ import org.apache.tapestry5.services.compatibility.DeprecationWarning; import org.apache.tapestry5.services.javascript.JavaScriptSupport; -import java.util.Collections; -import java.util.List; - /** * A mixin for a text field that allows for autocompletion of text fields. This is based on * Twttter typeahead.js version 0.10.5. @@ -61,6 +74,9 @@ public class Autocomplete @Environmental private JavaScriptSupport jsSupport; + + @Inject + private RequireJsModeHelper requireJsModeHelper; @Inject private TypeCoercer coercer; @@ -129,7 +145,7 @@ void afterRender() JSONObject spec = new JSONObject("id", field.getClientId(), "url", link.toString()).put("minChars", minChars).put("limit", maxSuggestions); - jsSupport.require("t5/core/autocomplete").with(spec); + requireJsModeHelper.importModule("t5/core/autocomplete").with(spec); } Object onAutocomplete(final EventContext context, @RequestParameter("t:input") diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Confirm.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Confirm.java index f67d203e8f..f412453384 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Confirm.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/Confirm.java @@ -17,6 +17,8 @@ import org.apache.tapestry5.annotations.Environmental; import org.apache.tapestry5.annotations.MixinAfter; import org.apache.tapestry5.annotations.Parameter; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; +import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.javascript.JavaScriptSupport; /** @@ -47,8 +49,8 @@ public class Confirm @Parameter("false") private boolean disabled; - @Environmental - private JavaScriptSupport javaScriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; /* * The CSS class for the ok button @@ -72,7 +74,7 @@ void beginRender(MarkupWriter writer) { if (!disabled) { - javaScriptSupport.require("t5/core/confirm-click"); + requireJsModeHelper.importModule("t5/core/confirm-click"); writer.attributes("data-confirm-title", title, "data-confirm-message", message, diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/TriggerFragment.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/TriggerFragment.java index 2d497b53e3..6055316ee0 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/TriggerFragment.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/TriggerFragment.java @@ -17,13 +17,13 @@ import org.apache.tapestry5.BindingConstants; import org.apache.tapestry5.ClientElement; import org.apache.tapestry5.Field; -import org.apache.tapestry5.annotations.Environmental; import org.apache.tapestry5.annotations.HeartbeatDeferred; import org.apache.tapestry5.annotations.InjectContainer; import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.corelib.components.FormFragment; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; +import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.json.JSONObject; -import org.apache.tapestry5.services.javascript.JavaScriptSupport; /** * A mixin that can be applied to a {@link org.apache.tapestry5.corelib.components.Checkbox} or @@ -53,8 +53,8 @@ public class TriggerFragment @Parameter private boolean invert; - @Environmental - private JavaScriptSupport javascriptSupport; + @Inject + private RequireJsModeHelper requireJsModeHelper; @HeartbeatDeferred void beginRender() @@ -73,6 +73,6 @@ void beginRender() spec.put("invert", true); } - javascriptSupport.require("t5/core/form-fragment").invoke("linkTrigger").with(spec); + requireJsModeHelper.importModule("t5/core/form-fragment").invoke("linkTrigger").with(spec); } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/ZoneRefresh.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/ZoneRefresh.java index 372618c5bd..e2e02fb7f3 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/ZoneRefresh.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/ZoneRefresh.java @@ -19,6 +19,7 @@ import org.apache.tapestry5.annotations.Parameter; import org.apache.tapestry5.corelib.components.Zone; import org.apache.tapestry5.http.Link; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.internal.util.CaptureResultCallback; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.services.javascript.JavaScriptSupport; @@ -57,17 +58,17 @@ public class ZoneRefresh private Zone zone; @Inject - private JavaScriptSupport javaScriptSupport; + private RequireJsModeHelper requireJsModeHelper; @Inject private ComponentResources resources; //For testing purpose - ZoneRefresh(Object[] context, ComponentResources resources, JavaScriptSupport javaScriptSupport, Zone zone) + ZoneRefresh(Object[] context, ComponentResources resources, RequireJsModeHelper requireJsModeHelper, Zone zone) { this.context = context; this.resources = resources; - this.javaScriptSupport = javaScriptSupport; + this.requireJsModeHelper = requireJsModeHelper; this.zone = zone; } @@ -76,7 +77,7 @@ void addJavaScript() { Link link = resources.createEventLink("zoneRefresh", context); - javaScriptSupport.require("t5/core/zone-refresh").with(zone.getClientId(), period, link.toString()); + requireJsModeHelper.importModule("t5/core/zone-refresh").with(zone.getClientId(), period, link.toString()); } Object onZoneRefresh(EventContext eventContext) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/alerts/AlertManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/alerts/AlertManagerImpl.java index 63ace7cf55..29f43d597f 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/alerts/AlertManagerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/alerts/AlertManagerImpl.java @@ -14,10 +14,13 @@ package org.apache.tapestry5.internal.alerts; +import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.alerts.*; import org.apache.tapestry5.http.services.Request; +import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.services.PerThreadValue; import org.apache.tapestry5.ioc.services.PerthreadManager; +import org.apache.tapestry5.json.JSONObject; import org.apache.tapestry5.services.ApplicationStateManager; import org.apache.tapestry5.services.ajax.AjaxResponseRenderer; import org.apache.tapestry5.services.ajax.JavaScriptCallback; @@ -32,12 +35,17 @@ public class AlertManagerImpl implements AlertManager private final AjaxResponseRenderer ajaxResponseRenderer; private final PerThreadValue needAlertStorageCleanup; + + private final boolean requireJsEnabled; - public AlertManagerImpl(ApplicationStateManager asm, Request request, AjaxResponseRenderer ajaxResponseRenderer, PerthreadManager perThreadManager) + public AlertManagerImpl(ApplicationStateManager asm, Request request, + AjaxResponseRenderer ajaxResponseRenderer, PerthreadManager perThreadManager, + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) boolean requireJsEnabled) { this.asm = asm; this.request = request; this.ajaxResponseRenderer = ajaxResponseRenderer; + this.requireJsEnabled = requireJsEnabled; needAlertStorageCleanup = perThreadManager.createValue(); } @@ -89,7 +97,15 @@ private void addCallbackForAlert(final Alert alert) { public void run(JavaScriptSupport javascriptSupport) { - javascriptSupport.require("t5/core/alert").with(alert.toJSON()); + final JSONObject json = alert.toJSON(); + if (requireJsEnabled) + { + javascriptSupport.require("t5/core/alert").with(json); + } + else + { + javascriptSupport.importEsModule("t5/core/alert").with(json); + } } }); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java index 5cf4dbd3bf..a065b4b6ca 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java @@ -22,12 +22,14 @@ import org.apache.tapestry5.BooleanHook; import org.apache.tapestry5.ComponentResources; import org.apache.tapestry5.FieldFocusPriority; +import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.commons.util.CollectionFactory; import org.apache.tapestry5.func.F; import org.apache.tapestry5.func.Worker; import org.apache.tapestry5.internal.InternalConstants; import org.apache.tapestry5.internal.services.DocumentLinker; import org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor; +import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.ioc.util.IdAllocator; import org.apache.tapestry5.json.JSONArray; @@ -77,11 +79,14 @@ public class JavaScriptSupportImpl implements JavaScriptSupport private String focusFieldId; private Map libraryURLToStackName, moduleNameToStackName; + + private final boolean requireJsEnabled; public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javascriptStackSource, - JavaScriptStackPathConstructor stackPathConstructor, BooleanHook suppressCoreStylesheetsHook) + JavaScriptStackPathConstructor stackPathConstructor, BooleanHook suppressCoreStylesheetsHook, + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) boolean requireJsEnabled) { - this(linker, javascriptStackSource, stackPathConstructor, new IdAllocator(), false, suppressCoreStylesheetsHook); + this(linker, javascriptStackSource, stackPathConstructor, new IdAllocator(), false, suppressCoreStylesheetsHook, requireJsEnabled); } /** @@ -105,7 +110,8 @@ public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javasc */ public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javascriptStackSource, JavaScriptStackPathConstructor stackPathConstructor, IdAllocator idAllocator, boolean partialMode, - BooleanHook suppressCoreStylesheetsHook) + BooleanHook suppressCoreStylesheetsHook, + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) boolean requireJsEnabled) { this.linker = linker; this.idAllocator = idAllocator; @@ -113,6 +119,7 @@ public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javasc this.stackPathConstructor = stackPathConstructor; this.partialMode = partialMode; this.suppressCoreStylesheetsHook = suppressCoreStylesheetsHook; + this.requireJsEnabled = requireJsEnabled; // In partial mode, assume that the infrastructure stack is already present // (from the original page render). @@ -467,4 +474,10 @@ public void addEsModuleConfigurationCallback(EsModuleConfigurationCallback callb linker.addEsModuleConfigurationCallback(callback); } + @Override + public boolean isRequireJsEnabled() + { + return requireJsEnabled; + } + } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelper.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelper.java new file mode 100644 index 0000000000..e752c8b20d --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelper.java @@ -0,0 +1,41 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.apache.tapestry5.internal.services.ajax; + +import org.apache.tapestry5.SymbolConstants; +import org.apache.tapestry5.services.javascript.AbstractInitialization; +import org.apache.tapestry5.services.javascript.JavaScriptSupport; + +/** + * Internal service to encapsulate the logic of calling + * {@linkplain JavaScriptSupport#require(String)} or + * {@linkplain JavaScriptSupport#importEsModule(String)} + * depending on the value of {@linkplain SymbolConstants#REQUIRE_JS_ENABLED}. + * Should only be used inside Tapestry itself. In user projects, + * explicit calls to the {@linkplain JavaScriptSupport} method should be used + * instead, since they'll be using just one mode (i.e. Require.js enabled or not). + * @since 5.10.0 + */ +public interface RequireJsModeHelper +{ + + /** + * Returns either {@linkplain JavaScriptSupport#require(String)} or + * {@linkplain JavaScriptSupport#importEsModule(String)} depending on the + * value of the {@linkplain SymbolConstants#REQUIRE_JS_ENABLED} configuration + * symbol value. + + * @param moduleName the module name or id. + * @return an {@linkplain AbstractInitialization} instance. + */ + AbstractInitialization importModule(String moduleName); +} \ No newline at end of file diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelperImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelperImpl.java new file mode 100644 index 0000000000..d3406b4b76 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/RequireJsModeHelperImpl.java @@ -0,0 +1,49 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.apache.tapestry5.internal.services.ajax; + +import org.apache.tapestry5.SymbolConstants; +import org.apache.tapestry5.ioc.annotations.Symbol; +import org.apache.tapestry5.services.javascript.AbstractInitialization; +import org.apache.tapestry5.services.javascript.JavaScriptSupport; + +public class RequireJsModeHelperImpl implements RequireJsModeHelper +{ + + private final JavaScriptSupport javaScriptSupport; + + private final boolean requireJsEnabled; + + public RequireJsModeHelperImpl( + final JavaScriptSupport javaScriptSupport, + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) final boolean requireJsEnabled) + { + this.javaScriptSupport = javaScriptSupport; + this.requireJsEnabled = requireJsEnabled; + } + + /** + * Returns either {@linkplain JavaScriptSupport#require(String)} or + * {@linkplain JavaScriptSupport#importEsModule(String)} depending on the + * value of the {@linkplain SymbolConstants#REQUIRE_JS_ENABLED} configuration + * symbol value. + + * @param moduleName the module name or id. + * @return an {@linkplain AbstractInitialization} instance. + */ + public AbstractInitialization importModule(String moduleName) + { + return requireJsEnabled ? + javaScriptSupport.require(moduleName) : + javaScriptSupport.importEsModule(moduleName); + } +} \ No newline at end of file diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java index 2ba6a1b3a6..ab036a5a8e 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/translator/NumericTranslatorSupportImpl.java @@ -14,12 +14,6 @@ package org.apache.tapestry5.internal.translator; -import org.apache.tapestry5.commons.services.TypeCoercer; -import org.apache.tapestry5.commons.util.CollectionFactory; -import org.apache.tapestry5.dom.Element; -import org.apache.tapestry5.ioc.services.ThreadLocale; -import org.apache.tapestry5.services.javascript.JavaScriptSupport; - import java.math.BigDecimal; import java.math.BigInteger; import java.text.DecimalFormat; @@ -29,22 +23,28 @@ import java.util.Locale; import java.util.Set; +import org.apache.tapestry5.commons.services.TypeCoercer; +import org.apache.tapestry5.commons.util.CollectionFactory; +import org.apache.tapestry5.dom.Element; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; +import org.apache.tapestry5.ioc.services.ThreadLocale; + public class NumericTranslatorSupportImpl implements NumericTranslatorSupport { private final TypeCoercer typeCoercer; private final ThreadLocale threadLocale; - private final JavaScriptSupport javascriptSupport; + private final RequireJsModeHelper requireJsModeHelper; private final Set integerTypes = CollectionFactory.newSet(); public NumericTranslatorSupportImpl(TypeCoercer typeCoercer, ThreadLocale threadLocale, - JavaScriptSupport javascriptSupport) + RequireJsModeHelper requireJsModeHelper) { this.typeCoercer = typeCoercer; this.threadLocale = threadLocale; - this.javascriptSupport = javascriptSupport; + this.requireJsModeHelper = requireJsModeHelper; Class[] integerTypes = {Byte.class, Short.class, Integer.class, Long.class, BigInteger.class}; @@ -60,7 +60,7 @@ public void setupTranslation(Class type, Element element, { String translation = isIntegerType(type) ? "integer" : "numeric"; - javascriptSupport.require("t5/core/validation"); + requireJsModeHelper.importModule("t5/core/validation"); element.attributes("data-validation", "true", "data-translation", translation, diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java index acaa253344..81dfe7dfff 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java @@ -29,6 +29,8 @@ import org.apache.tapestry5.internal.services.DocumentLinker; import org.apache.tapestry5.internal.services.ResourceStreamer; import org.apache.tapestry5.internal.services.ajax.JavaScriptSupportImpl; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelperImpl; import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker; import org.apache.tapestry5.internal.services.javascript.AddBrowserCompatibilityStyles; import org.apache.tapestry5.internal.services.javascript.ConfigureHTMLElementFilter; @@ -100,6 +102,7 @@ public static void bind(ServiceBinder binder) binder.bind(JavaScriptStackSource.class, JavaScriptStackSourceImpl.class); binder.bind(JavaScriptStack.class, ExtensibleJavaScriptStack.class).withMarker(Core.class).withId("CoreJavaScriptStack"); binder.bind(JavaScriptStack.class, ExtensibleJavaScriptStack.class).withMarker(Internal.class).withId("InternalJavaScriptStack"); + binder.bind(RequireJsModeHelper.class, RequireJsModeHelperImpl.class); } /** @@ -312,7 +315,8 @@ public static void setupModuleDispatchers(OrderedConfiguration confi public void exposeJavaScriptSupportForFullPageRenders(OrderedConfiguration configuration, final JavaScriptStackSource javascriptStackSource, final JavaScriptStackPathConstructor javascriptStackPathConstructor, - final Request request) + final Request request, + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) final boolean requireJsEnabled) { final BooleanHook suppressCoreStylesheetsHook = createSuppressCoreStylesheetHook(request); @@ -324,7 +328,7 @@ public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) DocumentLinker linker = environment.peekRequired(DocumentLinker.class); JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource, - javascriptStackPathConstructor, suppressCoreStylesheetsHook); + javascriptStackPathConstructor, suppressCoreStylesheetsHook, requireJsEnabled); environment.push(JavaScriptSupport.class, support); @@ -353,7 +357,9 @@ public void exposeJavaScriptSupportForPartialPageRender(OrderedConfiguration initialization = + javascriptSupport.isRequireJsEnabled() ? + javascriptSupport.require(moduleName) : + javascriptSupport.importEsModule(moduleName); + initialization.with("message", "Updated"); } }); diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java index d067f8cac6..a506478a93 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java @@ -186,6 +186,7 @@ public static void contributeApplicationDefaults(MappedConfiguration Date: Fri, 11 Jul 2025 15:03:15 -0300 Subject: [PATCH 02/30] TAP5-2810: ES modules for Bootstrap JS --- 5_10_RELEASE_NOTES.md | 20 +++++-- .../tapestry5/modules/JavaScriptModule.java | 55 +++++++++++++++++++ .../integration/app1/services/AppModule.java | 1 - 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/5_10_RELEASE_NOTES.md b/5_10_RELEASE_NOTES.md index d62d92163c..05f469737e 100644 --- a/5_10_RELEASE_NOTES.md +++ b/5_10_RELEASE_NOTES.md @@ -22,11 +22,21 @@ Scratch pad for changes destined for the 5.10.0 release notes page. # Non-backward-compatible changes -When using Require.js and AMD modules, from Tapestry 5.10.0 on, -the previously returned objects, functions or values are now -the `default` property of the object received from `require()`. -This is a consequence we couldn't avoid from the CoffeeScript -to JavaScript to TypeScript conversion. +* When using Require.js and AMD modules, from Tapestry 5.10.0 on, + the previously returned objects, functions or values are now + the `default` property of the object received from `require()`. + This is a consequence we couldn't avoid from the CoffeeScript + to JavaScript to TypeScript conversion. + + +# Notes about Require.js disabled mode +* When using Bootstrap 3, the `t5/bootstrap/*` modules had automatic dependency + management (for example, if you `bootstrap/tooltip`, `bootstrap/transition` + would automatically be included too through Require.js). This doesn't happen + when Require.js is disabled. So, for example, when importing `bootstrap/tooltip`, + you should import `bootstrap/trasition` first. Notice Bootstrap 3 JavaScript + files don't have any module management code on it (Require.js nor ES modules) + # Overall notes diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java index 81dfe7dfff..7c537d7ef0 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java @@ -14,6 +14,7 @@ import java.util.Locale; +import org.apache.tapestry5.Asset; import org.apache.tapestry5.BooleanHook; import org.apache.tapestry5.MarkupWriter; import org.apache.tapestry5.SymbolConstants; @@ -48,8 +49,10 @@ import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.services.FactoryDefaults; import org.apache.tapestry5.ioc.services.SymbolProvider; +import org.apache.tapestry5.ioc.services.SymbolSource; import org.apache.tapestry5.ioc.util.IdAllocator; import org.apache.tapestry5.json.JSONObject; +import org.apache.tapestry5.services.AssetSource; import org.apache.tapestry5.services.ComponentOverride; import org.apache.tapestry5.services.Core; import org.apache.tapestry5.services.Environment; @@ -542,6 +545,58 @@ public static void setupApplicationCatalogEsModules(OrderedConfiguration configuration, + @Symbol(SymbolConstants.BOOTSTRAP_ROOT) String bootstrapRoot, + Compatibility compatibility, + AssetSource assetSource) + { + + + if (compatibility.enabled(Trait.BOOTSTRAP_3)) + { + final String[] modules = new String[]{"affix", "alert", "button", "carousel", "collapse", "dropdown", "modal", + "scrollspy", "tab", "tooltip", "transition", "popover"}; + addEsBootstrap3Modules(configuration, modules, bootstrapRoot, assetSource); + } + if (compatibility.enabled(Trait.BOOTSTRAP_4)) + { + final String[] modules = new String[]{"alert", "button", "carousel", "collapse", "dropdown", "modal", + "scrollspy", "tab", "tooltip", "bootstrap-util", "popper"}; + + addEsBootstrap3Modules(configuration, modules, bootstrapRoot, assetSource); + } + + // Just the minimum to have alerts and AJAX validation working when Bootstrap + // is completely disabled + if (!compatibility.enabled(Trait.BOOTSTRAP_3) && !compatibility.enabled(Trait.BOOTSTRAP_4)) + { + final String[] modules = new String[]{"transition", "collapse", "alert", "dropdown"}; + addEsBootstrap3Modules(configuration, modules, bootstrapRoot, assetSource); + } + + } + + private static void addEsBootstrap3Modules( + OrderedConfiguration configuration, + String[] modules, + String bootstrapRoot, + AssetSource assetSource) + { + for (String module : modules) + { + final String moduleId = "bootstrap/" + module; + final Resource resource = assetSource.getClasspathAsset(bootstrapRoot + "/" + module + ".js") + .getResource(); + if (resource.exists()) + { + final String url = resource.toURL().toString(); + configuration.add(moduleId, EsModuleManagerContribution.base( + c -> EsModuleConfigurationCallback.setImport(c, moduleId, url))); + } + } + } /** * Contributes 'ConfigureHTMLElement', which writes the attributes into the HTML tag to describe locale, etc. diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java index a506478a93..d067f8cac6 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java @@ -186,7 +186,6 @@ public static void contributeApplicationDefaults(MappedConfiguration Date: Wed, 16 Jul 2025 14:56:01 -0300 Subject: [PATCH 03/30] Partial work on ES shims --- 5_10_RELEASE_NOTES.md | 1 + .../javascript/EsModuleManagerImpl.java | 4 +- .../services/javascript/EsShimDispatcher.java | 152 +++ .../tapestry5/modules/JavaScriptModule.java | 162 ++- .../services/javascript/EsModuleManager.java | 31 +- .../tapestry5/services/javascript/EsShim.java | 148 +++ .../underscore.js} | 0 .../integration/app1/pages/Index.java | 1150 ++++++++--------- 8 files changed, 1044 insertions(+), 604 deletions(-) create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java rename tapestry-core/src/main/resources/META-INF/assets/{tapestry5/underscore-esm.js => es-modules/underscore.js} (100%) diff --git a/5_10_RELEASE_NOTES.md b/5_10_RELEASE_NOTES.md index 05f469737e..b893fdec38 100644 --- a/5_10_RELEASE_NOTES.md +++ b/5_10_RELEASE_NOTES.md @@ -17,6 +17,7 @@ Scratch pad for changes destined for the 5.10.0 release notes page. * `org.apache.tapestry5.services.javascript.ImportPlacement` * `org.apache.tapestry5.services.javascript.EsModuleConfigurationCallback` * `org.apache.tapestry5.services.javascript.EsModuleManager` +* `org.apache.tapestry5.services.javascript.ESWrapper` # Non-backward-compatible changes (but that probably won't cause problems) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java index f65cc7a49b..a73a2fe642 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java @@ -73,7 +73,7 @@ public class EsModuleManagerImpl implements EsModuleManager private final List baseCallbacks; private final List globalPerRequestCallbacks; - + public EsModuleManagerImpl( List contributions, AssetSource assetSource, @@ -197,7 +197,7 @@ public void writeImports(Element root, List inits) init = (EsModuleInitializationImpl) i; final String moduleId = init.getModuleId(); - // Making sure the user doesn't shoot heir own foot + // Making sure the user doesn't shoot their own foot final String url = cache.get(moduleId); if (url == null) { diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java new file mode 100644 index 0000000000..fb1a24da9c --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java @@ -0,0 +1,152 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services.javascript; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.tapestry5.SymbolConstants; +import org.apache.tapestry5.commons.Resource; +import org.apache.tapestry5.http.services.Dispatcher; +import org.apache.tapestry5.http.services.Request; +import org.apache.tapestry5.http.services.Response; +import org.apache.tapestry5.internal.services.AssetDispatcher; +import org.apache.tapestry5.internal.services.ResourceStreamer; +import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker; +import org.apache.tapestry5.ioc.IOOperation; +import org.apache.tapestry5.ioc.OperationTracker; +import org.apache.tapestry5.ioc.annotations.Symbol; +import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration; +import org.apache.tapestry5.services.PathConstructor; +import org.apache.tapestry5.services.assets.StreamableResource; +import org.apache.tapestry5.services.assets.StreamableResourceProcessing; +import org.apache.tapestry5.services.assets.StreamableResourceSource; +import org.apache.tapestry5.services.javascript.EsShim; + +import jakarta.servlet.http.HttpServletResponse; + +/** + * Handler contributed to {@link AssetDispatcher} with key "es-shims". + * It interprets the extra path as a module name and serves the ES shim + * for it. + * + * @see EsShim + */ +@UsesMappedConfiguration(Resource.class) +public class EsShimDispatcher implements Dispatcher +{ + + private static final String ES_SUBPATH = "es-shims"; + + private final ResourceStreamer streamer; + + private final OperationTracker tracker; + + private final String requestPrefix; + + private final boolean compress; + + private Map shimMap; + + public EsShimDispatcher(Map configuration, + StreamableResourceSource streamableResourceSource, + ResourceChangeTracker resourceChangeTracker, + ResourceStreamer streamer, + OperationTracker tracker, + PathConstructor pathConstructor, + @Symbol(SymbolConstants.ASSET_PATH_PREFIX) + String assetPrefix, + boolean compress) + { + this.streamer = streamer; + this.tracker = tracker; + this.compress = compress; + this.shimMap = new HashMap<>(configuration.size()); + + try + { + for (String moduleName : configuration.keySet()) + { + shimMap.put(moduleName, streamableResourceSource.getStreamableResource( + configuration.get(moduleName), StreamableResourceProcessing.COMPRESSION_ENABLED, resourceChangeTracker)); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + + requestPrefix = pathConstructor.constructDispatchPath(assetPrefix + "/" + (compress ? ES_SUBPATH + ".gz" : ES_SUBPATH) + "/"); + } + + public boolean dispatch(Request request, Response response) throws IOException + { + String path = request.getPath(); + + if (path.startsWith(requestPrefix)) + { + String extraPath = path.substring(requestPrefix.length()); + + if (!handleModuleRequest(extraPath, response)) + { + response.sendError(HttpServletResponse.SC_NOT_FOUND, String.format("No ES module shim for path '%s'.", extraPath)); + } + + return true; + } + + return false; + + } + + public String getUrl(String moduleName) + { + return requestPrefix + moduleName; + } + + private boolean handleModuleRequest(String extraPath, Response response) throws IOException + { + int dotx = extraPath.lastIndexOf('.'); + + if (dotx < 0) + { + return false; + } + + if (!extraPath.substring(dotx + 1).equals("js")) + { + return false; + } + + final String moduleName = extraPath.substring(0, dotx); + + return tracker.perform(String.format("Streaming %s %s", + compress ? "compressed module" : "module", + moduleName), new IOOperation() + { + public Boolean perform() throws IOException + { + StreamableResource resource = shimMap.get(moduleName); + + if (resource != null) + { + return streamer.streamResource(resource, resource.getChecksum(), ResourceStreamer.DEFAULT_OPTIONS); + } + + return false; + } + }); + } + +} diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java index 7c537d7ef0..8b6bf81a02 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java @@ -36,6 +36,7 @@ import org.apache.tapestry5.internal.services.javascript.AddBrowserCompatibilityStyles; import org.apache.tapestry5.internal.services.javascript.ConfigureHTMLElementFilter; import org.apache.tapestry5.internal.services.javascript.EsModuleManagerImpl; +import org.apache.tapestry5.internal.services.javascript.EsShimDispatcher; import org.apache.tapestry5.internal.services.javascript.Internal; import org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor; import org.apache.tapestry5.internal.services.javascript.JavaScriptStackSourceImpl; @@ -66,6 +67,7 @@ import org.apache.tapestry5.services.compatibility.Compatibility; import org.apache.tapestry5.services.compatibility.Trait; import org.apache.tapestry5.services.javascript.AMDWrapper; +import org.apache.tapestry5.services.javascript.EsShim; import org.apache.tapestry5.services.javascript.EsModuleConfigurationCallback; import org.apache.tapestry5.services.javascript.EsModuleManager; import org.apache.tapestry5.services.javascript.EsModuleManager.EsModuleManagerContribution; @@ -106,6 +108,7 @@ public static void bind(ServiceBinder binder) binder.bind(JavaScriptStack.class, ExtensibleJavaScriptStack.class).withMarker(Core.class).withId("CoreJavaScriptStack"); binder.bind(JavaScriptStack.class, ExtensibleJavaScriptStack.class).withMarker(Internal.class).withId("InternalJavaScriptStack"); binder.bind(RequireJsModeHelper.class, RequireJsModeHelperImpl.class); + binder.bind(EsShimDispatcher.class); } /** @@ -289,7 +292,8 @@ public static void setupModuleDispatchers(OrderedConfiguration confi @Symbol(SymbolConstants.MODULE_PATH_PREFIX) String modulePathPrefix, @Symbol(SymbolConstants.ASSET_PATH_PREFIX) - String assetPathPrefix) + String assetPathPrefix, + EsShimDispatcher esShimDispatcher) { configuration.add("Modules", new ModuleDispatcher(moduleManager, resourceStreamer, tracker, pathConstructor, @@ -302,6 +306,9 @@ public static void setupModuleDispatchers(OrderedConfiguration confi javaScriptStackSource, javaScriptStackPathConstructor, localizationSetter, modulePathPrefix, assetPathPrefix, true), "after:Modules", "before:ComponentEvent"); + + configuration.add("EsShims", esShimDispatcher, "after:Asset", "before:ComponentEvent"); + } /** @@ -544,56 +551,163 @@ public static void setupApplicationCatalogEsModules(OrderedConfiguration configuration, - @Symbol(SymbolConstants.BOOTSTRAP_ROOT) String bootstrapRoot, + + @Contribute(EsShimDispatcher.class) + public static void setupBaseEsShims( + MappedConfiguration configuration, + @Path("${tapestry.asset.root}/bootstrap/js/transition.js") + Resource transition, + @Path("${tapestry.asset.root}/bootstrap4/js/bootstrap-util.js") + Resource bootstrapUtil, Compatibility compatibility, - AssetSource assetSource) + AssetSource assetSource, + EsShimDispatcher esShimDispatcher) { +// configuration.add("jquery", new JavaScriptModuleConfiguration(jqueryShim)); if (compatibility.enabled(Trait.BOOTSTRAP_3)) { final String[] modules = new String[]{"affix", "alert", "button", "carousel", "collapse", "dropdown", "modal", - "scrollspy", "tab", "tooltip", "transition", "popover"}; - addEsBootstrap3Modules(configuration, modules, bootstrapRoot, assetSource); + "scrollspy", "tab", "tooltip"}; + addBootstrap3EsShims(configuration, modules, transition); + + Resource popover = transition.forFile("popover.js"); + + final String popoverModuleName = "bootstrap/popover"; + configuration.add(popoverModuleName, + EsModuleManagerContribution.base( + new EsShim(popover).importModule("bootstrap/tooltip"), + createCallback(popoverModuleName, esShimDispatcher))); } + if (compatibility.enabled(Trait.BOOTSTRAP_4)) { - final String[] modules = new String[]{"alert", "button", "carousel", "collapse", "dropdown", "modal", - "scrollspy", "tab", "tooltip", "bootstrap-util", "popper"}; - - addEsBootstrap3Modules(configuration, modules, bootstrapRoot, assetSource); + final String bootstrapUtilModuleName = "bootstrap/bootstrap-util"; + configuration.add(bootstrapUtilModuleName, + EsModuleManagerContribution.base( + new EsShim(bootstrapUtil), + createCallback(bootstrapUtilModuleName, esShimDispatcher))); + + final String popperModuleName = "bootstrap/popper"; + final Resource popper = bootstrapUtil.forFile("popper.js"); + configuration.add(popperModuleName, + EsModuleManagerContribution.base( + new EsShim(popper), + createCallback(popperModuleName, esShimDispatcher))); + + for (String name : new String[]{"alert", "button", "carousel", "collapse", "dropdown", "modal", + "scrollspy", "tab", "tooltip"}) + { + Resource lib = bootstrapUtil.forFile(name + ".js"); + if (lib.exists()) + { + final String moduleName = "bootstrap/" + name; + configuration.add(moduleName, + EsModuleManagerContribution.base( + new EsShim(lib) + .importModule(bootstrapUtilModuleName) + .importModule(popperModuleName), + createCallback(popperModuleName, esShimDispatcher))); + } + } } // Just the minimum to have alerts and AJAX validation working when Bootstrap // is completely disabled if (!compatibility.enabled(Trait.BOOTSTRAP_3) && !compatibility.enabled(Trait.BOOTSTRAP_4)) { - final String[] modules = new String[]{"transition", "collapse", "alert", "dropdown"}; - addEsBootstrap3Modules(configuration, modules, bootstrapRoot, assetSource); + final String[] modules = new String[]{"alert", "dropdown", "collapse"}; + addBootstrap3EsShims(configuration, modules, transition); } + } +// @Contribute(EsModuleManager.class) +// public static void setupBaseEsModules( +// OrderedConfiguration configuration, +// @Path("${tapestry.asset.root}/bootstrap/js/transition.js") +// Resource transition, +// @Path("${tapestry.asset.root}/bootstrap4/js/bootstrap-util.js") +// Resource bootstrapUtil, +// Compatibility compatibility, +// AssetSource assetSource, +// EsShimDispatcher esShimDispatcher) +// { +// +//// configuration.add("jquery", new JavaScriptModuleConfiguration(jqueryShim)); +// +// if (compatibility.enabled(Trait.BOOTSTRAP_3)) +// { +// final String[] modules = new String[]{"affix", "alert", "button", "carousel", "collapse", "dropdown", "modal", +// "scrollspy", "tab", "tooltip"}; +// addBootstrap3EsShims(configuration, modules, transition); +// +// Resource popover = transition.forFile("popover.js"); +// +// final String popoverModuleName = "bootstrap/popover"; +// configuration.add(popoverModuleName, +// EsModuleManagerContribution.base( +// new EsShim(popover).importModule("bootstrap/tooltip"), +// createCallback(popoverModuleName, esShimDispatcher))); +// } +// +// if (compatibility.enabled(Trait.BOOTSTRAP_4)) +// { +// final String bootstrapUtilModuleName = "bootstrap/bootstrap-util"; +// configuration.add(bootstrapUtilModuleName, +// EsModuleManagerContribution.base( +// new EsShim(bootstrapUtil), +// createCallback(bootstrapUtilModuleName, esShimDispatcher))); +// +// final String popperModuleName = "bootstrap/popper"; +// final Resource popper = bootstrapUtil.forFile("popper.js"); +// configuration.add(popperModuleName, +// EsModuleManagerContribution.base( +// new EsShim(popper), +// createCallback(popperModuleName, esShimDispatcher))); +// +// for (String name : new String[]{"alert", "button", "carousel", "collapse", "dropdown", "modal", +// "scrollspy", "tab", "tooltip"}) +// { +// Resource lib = bootstrapUtil.forFile(name + ".js"); +// if (lib.exists()) +// { +// final String moduleName = "bootstrap/" + name; +// configuration.add(moduleName, +// EsModuleManagerContribution.base( +// new EsShim(lib) +// .importModule(bootstrapUtilModuleName) +// .importModule(popperModuleName), +// createCallback(popperModuleName, esShimDispatcher))); +// } +// } +// } +// +// // Just the minimum to have alerts and AJAX validation working when Bootstrap +// // is completely disabled +// if (!compatibility.enabled(Trait.BOOTSTRAP_3) && !compatibility.enabled(Trait.BOOTSTRAP_4)) +// { +// final String[] modules = new String[]{"alert", "dropdown", "collapse"}; +// addBootstrap3EsShims(configuration, modules, transition); +// } +// } + + private static EsModuleConfigurationCallback createCallback(final String moduleName, EsShimDispatcher esShimDispatcher) { + return c -> EsModuleConfigurationCallback.setImport(c, moduleName, esShimDispatcher.getUrl(moduleName)); } - private static void addEsBootstrap3Modules( - OrderedConfiguration configuration, + private static void addBootstrap3EsShims( + MappedConfiguration configuration, String[] modules, - String bootstrapRoot, - AssetSource assetSource) + Resource reference) { for (String module : modules) { final String moduleId = "bootstrap/" + module; - final Resource resource = assetSource.getClasspathAsset(bootstrapRoot + "/" + module + ".js") - .getResource(); + final Resource resource = reference.forFile(module + ".js"); if (resource.exists()) { - final String url = resource.toURL().toString(); - configuration.add(moduleId, EsModuleManagerContribution.base( - c -> EsModuleConfigurationCallback.setImport(c, moduleId, url))); + configuration.add(moduleId, resource); } } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java index 70a3b00b08..edd3dfbb01 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java @@ -67,12 +67,16 @@ public final class EsModuleManagerContribution private final EsModuleConfigurationCallback callback; private final boolean isBase; + + // In case this is an ES shim contribution. + private final EsShim esWrapper; - EsModuleManagerContribution(EsModuleConfigurationCallback callback, boolean isBase) + private EsModuleManagerContribution(EsModuleConfigurationCallback callback, boolean isBase, EsShim esWrapper) { super(); this.callback = callback; this.isBase = isBase; + this.esWrapper = esWrapper; } /** @@ -83,9 +87,26 @@ public final class EsModuleManagerContribution */ public static EsModuleManagerContribution base(EsModuleConfigurationCallback callback) { - return new EsModuleManagerContribution(callback, true); + return new EsModuleManagerContribution(callback, true, null); } + /** + * Creates a base contribution (one that contributes a callback used + * when creating the base import map to be used for all requests) + * which is also an ES module shim. + * @param esWrapper an {@linkplain EsShim} instance. It cannot be null. + * @param callback an {@linkplain EsModuleConfigurationCallback} instance. + * @return a corresponding {@linkplain EsModuleManagerContribution}. + */ + public static EsModuleManagerContribution base(EsShim esWrapper, EsModuleConfigurationCallback callback) + { + if (esWrapper == null) + { + throw new IllegalArgumentException("Parameter esWrapper cannot be null"); + } + return new EsModuleManagerContribution(callback, true, esWrapper); + } + /** * Creates a global per-request contribution (one that contributes a callback used * in all requests after the callbacks added through @@ -96,7 +117,7 @@ public static EsModuleManagerContribution base(EsModuleConfigurationCallback cal */ public static EsModuleManagerContribution globalPerRequest(EsModuleConfigurationCallback callback) { - return new EsModuleManagerContribution(callback, false); + return new EsModuleManagerContribution(callback, false, null); } public EsModuleConfigurationCallback getCallback() { @@ -106,6 +127,10 @@ public EsModuleConfigurationCallback getCallback() { public boolean isBase() { return isBase; } + + public EsShim getEsWrapper() { + return esWrapper; + } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java new file mode 100644 index 0000000000..ca20f71b45 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java @@ -0,0 +1,148 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.services.javascript; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.tapestry5.commons.Resource; +import org.apache.tapestry5.internal.util.VirtualResource; + +/** + * Used to wrap plain JavaScript libraries as ES modules. The underlying + * resource is transformed before it is sent to the client. + * + * @since 5.10.0 + */ +public class EsShim { + + /** + * The underlying resource, usually a JavaScript library + */ + private final Resource resource; + + /** + * The modules that this module requires, the keys being module names and + * the values being the respective parameter names for the module's factory + * function. + */ + private final Map importConfig = new LinkedHashMap(); + + public EsShim(final Resource resource) { + this.resource = resource; + } + + /** + * Adds a dependency on another module. + * + * @param moduleName + * the name of the required module, e.g. jQuery + * @param variableName + * the module's corresponding variable name + * @return this ESWrapper for further configuration + */ + public EsShim importModule(final String moduleName, + final String variableName) { + importConfig.put(moduleName, variableName); + return this; + } + + /** + * Adds a dependency on another module. The module will be loaded but not + * passed to a variable. This is useful for dependencies on other + * modules that do not actually return a value. + * + * @param moduleName + * the name of the required module, e.g. + * bootstrap/transition + * @return this ESWrapper for further configuration + */ + public EsShim importModule(final String moduleName) { + importConfig.put(moduleName, null); + return this; + } + + /** + * Returns a virtual resource representing this wrapper. + * @return a {@linkplain Resource}. + */ + public Resource getResource() { + return new ESModuleWrapperResource(resource, importConfig); + } + + /** + * A virtual resource that wraps a plain JavaScript library as an AMD + * module. + * + */ + private final static class ESModuleWrapperResource extends VirtualResource + { + private final Resource resource; + private final Map importConfig; + + public ESModuleWrapperResource(final Resource resource, + final Map importConfig) + { + this.resource = resource; + this.importConfig = importConfig; + } + + @Override + public InputStream openStream() throws IOException + { + StringBuilder sb = new StringBuilder(); + + for (String module : importConfig.keySet()) + { + final String variableName = importConfig.get(module); + sb.append("import "); + if (variableName != null) + { + sb.append(variableName); + sb.append(" from "); + } + sb.append("\""); + sb.append(module); + sb.append("\";\n"); + } + + return new SequenceInputStream(toInputStream(sb), resource.openStream()); + } + + @Override + public String getFile() { + return "generated-module-for-" + resource.getFile(); + } + + @Override + public URL toURL() { + return null; + } + + @Override + public String toString() { + return "ES module wrapper for " + resource.toString(); + } + + private static InputStream toInputStream(final StringBuilder sb) + { + return new ByteArrayInputStream(sb.toString().getBytes(UTF8)); + } + } + +} diff --git a/tapestry-core/src/main/resources/META-INF/assets/tapestry5/underscore-esm.js b/tapestry-core/src/main/resources/META-INF/assets/es-modules/underscore.js similarity index 100% rename from tapestry-core/src/main/resources/META-INF/assets/tapestry5/underscore-esm.js rename to tapestry-core/src/main/resources/META-INF/assets/es-modules/underscore.js diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java index fcad59e0ab..42338f94ab 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java @@ -58,581 +58,581 @@ public int compareTo(Item o) private static final List ITEMS = CollectionFactory .newList( - new Item("PublishEventDemo", "@PublishEvent Demo", "Publishing server-side events to client-side code (JavaScript)"), - - new Item("StaticActivationContextValueDemo", "@StaticActivationContextValue Demo", "Demonstrates the usage of @StaticActivationContextValue"), - - new Item("rest/RestWithOnEventDemo", "REST with @OnEvent Demo", "Demonstrates the usage of @OnEvent to handle REST requests"), - - new Item("rest/RestWithEventHandlerMethodNameDemo", "REST with Event Handler Method Name Demo", "Demonstrates the usage of event handler method names to handle REST requests"), - - new Item("Html5DateFieldDemo", "Html5DateField Demo", "Choosing dates using the native HTML5 date picker"), - -// new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."), - - new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."), - - new Item("OverrideEventHandlerDemo", "Event Handler Override Demo", "Event Handler methods overridden by sub-classes invoke base-class correctly."), - - new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."), - - new Item("MissingRequiredARP", "Missing Query Parameter for @ActivationRequestParameter", "Activating a page with a required @ActivationRequestParameter, but no matching query parameter, is an error."), - -// new Item("DateFieldValidationDemo", "DateField Validation Demo", -// "Use of DateField component when client validation is disabled."), - - new Item("MixinParameters54", "Strict Mixin Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the mixin id."), - - new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."), - - new Item("FormCancelActionDemo", "Form Cancel Action Demo", "FormSupport.addCancel() support"), - - new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio components inside an Ajax form"), - - new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"), - - new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"), - - new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."), - - new Item("MissingAssetDemo", "Missing Asset Demo", "Error when injecting an asset that does not exist."), - - new Item("ConfirmDemo", "Confirm Mixin Demo", "Confirm an action when clicking it."), - - new Item("SingleErrorDemo", "Single Error", "Using Error component to customize where the errors for a field will be displayed."), - - new Item("JavaScriptTests", "JavaScript Tests", "Client-side tests using Mocha and Chai"), - - new Item("ModuleInitDemo", "Module-based Initialization Demo", "Invoke a module function to perform page initialization"), - - new Item("OperationWorkerDemo", "Operation Worker Demo", "Demonstrate use of @Operation annotation on component methods"), - - new Item("MixinParameterDefault", "Mixin Parameter with Default", "Ensure that a mixin parameter with a default value is not reported as unbound."), - - new Item("MixinVsInformalParameter", "Mixin Parameter vs. Informal Parameter", "Informal Paramters vs. Mixin parameter of same name"), - - new Item("inherit/childa", "TAP5-1656 Demo", "Test a reported bug in component inheritance"), - - new Item("ComponentInsideBlockDemo", "Component Inside Block Demo", "Verify that a component, inside a block, is still an embedded "), - - new Item("EventMethodUnmatchedComponentId", "Unmatched Component Id in Event Method Demo", "Show that referencing a component that does not exist in an event handler method name is an error."), - - new Item("AlertsDemo", "Alerts Demo", "Managing alerts both traditional and Ajax"), - - new Item("ClientConsoleDemo", "Client Console Demo", "Demo for the JavaScript client-side console"), - - new Item("InvalidFormalParameterDemo", "Unmatched Formal Parameter with @Component", "Parameters specified with @Component annotation must match formal parameters"), - - new Item("NullBindingToPrimitive", "Null Bound to Primitive Demo", "Correct exception when a primitive parameter is bound to null"), - - new Item("TreeDemo", "Tree Component Demo", "Demo of Tree Component"), - - new Item("TreeSelectionDemo", "Tree Component Selection Demo", "Demo of Selection with Tree Component"), - - new Item("TreeNoRootsDemo", "Tree Component No Roots Demo", "Demo of No Roots with Tree Component"), - - new Item("InvalidExpressionInDynamicTemplate", "Invalid Dynamic Expression", - "Invalid expression in a Dynamic Template"), - - new Item("DynamicDemo", "Dynamic Demo", "Basic Dynamic component tests"), - - new Item("DynamicExpansionsDemo", "Expansions in Dynamic Templates", - "Expansions inside Dynamic component content and attributes"), - - new Item("PACAnnotationDemo", "PageActivationContext Demo", - "Shows that @PageActivationContext fields are set before calls to the activate event handler."), - - new Item("PACMultipleAnnotationDemo", "PageActivationContext Multiple Demo", - "Demonstrates multiple @PageActivationContext fields."), - - new Item("PublicFieldAccessDemo", "Public Field Access Demo", "Demonstrates TAP5-1222 fix"), - - new Item("ActivationRequestParameterDemo", "ActivationRequestParameter Annotation Demo", - "Use of @ActivationRequestParameter to encode page state into query parameters"), - - new Item("LibraryMessagesDemo", "Library Messages Demo", - "Demo ability to contribute additional message catalog resources to the application global catalog."), - - new Item("MultiZoneUpdateInsideForm", "MultiZone Update inside a Form", - "Update multiple zones within a single Form."), - - new Item("ZoneFormUpdateDemo", "Zone/Form Update Demo", "Updating a Zone inside a Form"), - - new Item("MultiZoneStringBodyDemo", "MultiZone String Body Demo", - "Multi-zone updates in a loop using strings coerced into blocks"), - - new Item("RenderNotificationDemo", "RenderNotification Demo", "Use of RenderNotification mixin"), - - new Item("InjectMessagesDemo", "Inject Global Messages into Service Demo", - "Ensure that it is possible to inject the application global message catalog into a service"), - - new Item("ReloadDemo", "Reloadable Service Implementation Demo", - "Used when manually testing service reloads"), - - new Item("RequestParameterDemo", "RequestParameter Annotation Demo", - "Use of @RequestParameter annotation on event handler method parameters"), - - new Item("CancelDemo", "Cancel Demo", "Use of the cancel option with Submit"), - - new Item("CanceledEventDemo", "Canceled Event Demo", "Triggering of the canceled event from a form."), - - new Item("PageResetDemo", "PageReset Annotation Demo", - "Use of PageReset annotation to re-initialize page state"), - - new Item("TestOnlyServiceDemo", "Test Only Service Demo", - "IoC module available via web.xml configuration"), - - new Item("RenderObjectExceptionDemo", "RenderObject Exception Demo", - "Demonstrate how exceptions when rendering default objects are displayed."), - - new Item("MultiLevelInheritDemo", "Multi-Level Inherit Demo", - "Use of inherit: binding prefix across three levels"), - - new Item("HiddenDemo", "Hidden Demo", "Demo the use of the Hidden component."), - - new Item("FormZoneDemo", "Form Zone Demo", "Use a form to update a zone."), - - new Item("ZoneUpdateNamespace", "Zone/Namespace Interaction", "Prove that TAP5-573 is fixed"), - - new Item("AbstractComponentDemo", "Abstract Component Demo", "Error when a component is abstract"), - - new Item("TemplateOverrideDemo", "Template Override Demo", - "Child component extends and overrides parent template."), - - new Item("MultiZoneUpdateDemo", "Multiple Zone Update Demo", - "A single request can now update multiple Zones"), - - new Item("LinkSubmitInZoneDemo", "LinkSubmit inside Zone", - "Ensure that a LinkSubmit works correctly when its containing Form updates a Zone"), - - new Item("ProgressiveDemo", "ProgressiveDisplay Demo", "Progressive Enhancement via a component"), - - new Item("ClientNumericValidationDemo", "Client-Side Numeric Validation", - "Client-side locale-specific validation"), - - new Item("PublishParametersDemo", "Publish Parameters Demo", - "Use of @Component.publishParameters attribute."), - - new Item("LinkSubmitDemo", "LinkSubmit Demo", "JavaScript LinkSubmit component"), - - new Item("LinkSubmitWithoutValidatorDemo", "LinkSubmit Without Validator Demo", - "Demonstrates that the LinkSubmit component is working without a validator on any of fields in the form"), - - new Item("PerFormValidationMessageDemo", "Per-Form Validation Messages", - "Per-form configuration of validation messages and constraints."), - - new Item("EmptyLoopDemo", "Empty Loop Demo", "Use of empty parameter with the Loop component."), - - new Item("GenericLoopDemo", "Generic Loop Demo", - "Use of generic parameters with the Loop component."), - - new Item("LoopWithMixinDemo", "Loop With Mixin Demo", - "Use a mixin with a Loop component."), - - new Item("BlankPasswordDemo", "Blank Password Demo", - "Show that a blank value in a PasswordField does not update the server side value."), - - new Item("GridFormEncoderDemo", "Grid Form Encoder Demo", - "Grid inside a Form using the ValueEncoder option"), - - new Item("GridFormWithInitialSortMixinDemo", "Grid Form With Initial Sort Mixin Demo", - "Grid inside a Form using the InitialSort mixin"), - - new Item("DateFieldAjaxFormLoop", "DateField inside AjaxFormLoop", - "Show that DateField component works correctly inside AjaxFormLoop"), - - new Item("NestedForm", "Nested Form Demo", "Error when a Form is nested inside another Form."), - - new Item("UnhandledEventDemo", "Unhandled Event Demo", - "Events that don't have matching event handlers cause exceptions"), - - new Item("PrimitiveDefaultDemo", "Primitive Default Demo", - "Primitive value returned from parameter default method"), - - new Item("ValidateFormValidationExceptionDemo", "ValidationForm ValidationException Demo", - "Throwing a ValidationException from the validateForm event handler."), - - new Item("ClientFormatDemo", "Client Format Validation", "Client-side input format validation"), - - new Item("ShortGrid", "Short Grid", - "Grid where the number of claimed rows is less than the number of actual rows"), - - new Item("NullParameterDemo", "Null Parameter Demo", "Binding a not-null parameter to null."), - - new Item("nestedbeaneditor", "Nested BeanEditor", - "BeanEditor as override for property editor in BeanEditForm"), - - new Item("actionpage", "Action Page", "tests fixture for ActionLink component"), - - new Item("cleancachedemo", "Clean Cache Demo", "cache cleared properly during Ajax calls"), - - new Item("numberbeaneditordemo", "Number BeanEditor Demo", - "use of nulls and wrapper types with BeanEditor"), - - new Item("forminjectordemo", "FormInjector Demo", "extending a form dynamically via Ajax"), - - new Item("music", "Music Page", "demo handling of edge cases of page naming"), - - new Item("PersistentDemo", "Persistent Demo", "storing and clearing persistent properties"), - - new Item("ActionViaLinkDemo", "Action via Link Demo", "tests creating an action link explicitly"), - - new Item("FormFragmentDemo", "Form Fragment Demo", "page with dynamic form sections"), - - new Item("BooleanDemo", "Boolean Property Demo", - "demo boolean properties using both is and get prefixes"), - - new Item("DeleteFromGridDemo", "Delete From Grid", "demo deleting items form a Grid"), - - new Item("RenderErrorDemo", "Render Error Demo", "reporting of errors while rendering"), - - new Item("nested/AssetDemo", "AssetDemo", "declaring an image using Assets"), - - new Item("nested/ActionDemo", "Action With Context Demo", - "using action links with context on page with activation context"), - - new Item("blockdemo", "BlockDemo", "use of blocks to control rendering"), - - new Item("countdown", "Countdown Page", "defining component using @Component annotation"), - - new Item("injectdemo", "Inject Demo", "use of various kinds of injection"), - - new Item("instancemixin", "InstanceMixin", "mixin added to a particular component instance"), - - new Item("TextFieldWrapperTypeDemo", "TextField Wrapper Types", - "use of TextField to edit numeric wrapper types (not primitives) "), - - new Item("EnvironmentalDemo", "Environmental Annotation Usage", - "Storing and retrieving Environmental values"), - - new Item("Expansion", "Expansion Page", "Use of expansions in templates"), - - new Item("ExpansionSubclass", "ExpansionSubclass", - "components can inherit templates from base classes"), - - new Item("Localization", "Localization", "access localized messages from the component catalog"), - - new Item("NumberSelect", "NumberSelect", "passivate/activate page context demo"), - - new Item("ParameterConflict", "Template Overridden by Class Page", - "Parameters in the class override those in the template"), - - new Item("ParameterDefault", "ParameterDefault", "defaulter methods for component parameters"), - - new Item("passwordfielddemo", "PasswordFieldDemo", "test for the PasswordField component"), - - new Item("rendercomponentdemo", "RenderComponentDemo", - "components that \"nominate\" other components to render"), - - new Item("renderphaseorder", "RenderPhaseOrder", - "order of operations when invoking render phase methods"), - - new Item("simpleform", "SimpleForm", "first pass at writing Form and TextField components"), - - new Item("OptionGroupForm", "OptionGroupForm Demo", "Select with Option Group"), - - new Item("validform", "ValidForm", "server-side input validation"), - - new Item("ToDoListVolatile", "ToDo List (Volatile)", "Loops and Submit inside Form, volatile mode"), - - new Item("MissingTemplate", "Missing Template Demo", - "Demo for what happens when a template is not found for a page"), - - new Item("nested/zonedemo", "Zone Demo", "dynamic updates within a page"), - - new Item("todolist", "ToDo List", "Loops and Submit inside Form using primary key encoder"), - - new Item("flashdemo", "FlashDemo", "demonstrate 'flash' persistence"), - - new Item("beaneditordemo", "BeanEditor Demo", "demonstrate the BeanEditor mega-component"), - - new Item("pageloadeddemo", "PageLoaded Demo", "shows that page lifecycle methods are invoked"), - - new Item("griddemo", "Grid Demo", "default Grid component"), - - new Item("GridInLoopDemo", "Grid In Loop Demo", "Grid inside loop with different model on each iteration"), - - new Item("nullgrid", "Null Grid", "handling of null source for Grid"), - - new Item("gridsetdemo", "Grid Set Demo", "handling of Set sources for Grid"), - - new Item("gridenumdemo", "Grid Enum Demo", "handling of enum types in the Grid"), - - new Item("GridRemoveReorderDemo", "Grid Remove/Reorder Demo", - "handling of remove and reorder parameters"), - - new Item("EmptyGrid", "Empty Grid Demo", "show table for empty data sources"), - - new Item("GridEarlyPagingDemo", "Grid Early Paging", "set a Grid's current page before rendering"), - - new Item("protected", "Protected Page", - "Demonstrate result of non-void return from a page's activate method"), - - new Item("Kicker", "Kicker", "demos complex page and component context in links"), - - new Item("simpletrackgriddemo", "SimpleTrack Grid Demo", - "customizing the model for a Grid around an interface"), - - new Item("pagelinkcontext", "PageLink Context Demo", - "passing explicit context in a page render link"), - - new Item("pagecontextinform", "Page Context in Form", "passivate/activate page context in Form", - "betty", "wilma", "context with spaces", "context/with/slashes"), - - new Item("ValidBeanEditorDemo", "Client Validation Demo", "BeanEditor with validation enabled"), - - new Item("Unreachable", "Unreachable Page", "page not reachable due to IgnoredPathsFilter"), - - new Item("renderabledemo", "Renderable Demo", - "shows that render phase methods can return a Renderable"), - - new Item("inheritedbindingsdemo", "Inherited Bindings Demo", - "Tests for components that inherit bindings from containing components"), - - new Item("ClientPersistenceDemo", "Client Persistence Demo", - "component field values persisted on the client side"), - - new Item("attributeExpansionsDemo", "Attribute Expansions Demo", - "use expansions inside attributes of ordinary elements"), - - new Item("PaletteDemo", "Palette Demo", "multiple selection component"), - new Item("PaletteGroupedDemo", "Palette Grouped Demo", "multiple selection component (grouped)"), - - new Item("ReturnTypes", "Return Types", "tests various event handler return types"), - - new Item("FormEncodingType", "Form Encoding Type", - "Test ability to set an encoding type for a Form"), - - new Item("RadioDemo", "RadioDemo", "Use of the RadioGroup and Radio components"), - - new Item("RegexpDemo", "Regexp Demo", "Use of the Regexp validator"), - - new Item("BeanEditRemoveReorder", "BeanEdit Remove/Reorder", - "Use of the remove and reorder parameters with BeanEditForm"), - - new Item("MultiBeanEditDemo", "MultiBeanEdit Demo", - "Multiple BeanEditor components in a single form"), - - new Item("GridFormDemo", "Grid Form Demo", "Grid operating inside a Form"), - - new Item("DateFieldDemo", "DateField Demo", "using DateField by itself on a page"), - - new Item("BeanEditDateDemo", "BeanEditor / Date Demo", - "Use of date properties inside BeanEditor and BeanDisplay"), - - new Item("eventmethodtranslate", "EventMethod Translator", - "Demo ability to provide toclient and parseclient event handler methods"), - - new Item("autocompletedemo", "Autocomplete Mixin Demo", - "Demo the autocomplete mixin for text fields"), - - new Item("componentparameter", "ComponentParameter Demo", - " Demo using a component type as a parameter type and succesfully passing a component"), - - new Item("inheritinformalsdemo", "Inherit Informal Parameters Demo", - "Demo a component which inherits informal parameters from its container"), - - new Item("disabledfields", "Disabled Fields", - "Demonstrate a bunch of disabled fields, to verify that the RenderDisabled mixin works and is being used properly"), - - new Item("BeanEditorOverride", "BeanEditor Override", - "Property editor overrides work for the BeanEditor component itself (not just the BeanEditForm component)"), - - new Item("varbindingdemo", "Var Binding Demo", "use of the var: binding prefix"), - - new Item("leangriddemo", "Lean Grid Demo", - "Grid component with lean parameter turned on, to eliminate CSS class attributes in TD and TH elements"), - - new Item("blockcaller", "Action Links off of Active Page", - "Actions can exist on pages other than the active page, via Blocks."), - - new Item("unlessdemo", "Unless Demo", "use of the Unless component"), - - new Item("delegateinline", "Inline Delegate", - "Using the delegate component to create inline components"), - - new Item("MagicValueEncoder", "Magic ValueEncoder Demo", - "Automatic creation of ValueEncoder using the TypeCoercer"), - - new Item("NullStrategyDemo", "Null Field Strategy Demo", "use of the nulls parameter of TextField"), - - new Item("OverrideValidationDecorator", "Override Validation Decorator", - "override the default validation decorator"), - - new Item("ExceptionEventDemo", "Exception Event Demo", "handling component event exceptions"), - - new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"), - - new Item("PrimitiveArrayParameterDemo", "Primitive Array Parameter Demo", - "use primitive array as parameter type"), - - new Item("RenderPhaseMethodExceptionDemo", "Render Phase Method Exception Demo", - "render phase methods may throw checked exceptions"), - - new Item("TrackEditor", "Generic Page Class Demo", - "demo use of generics with component classes and, particularily, with property types"), - - new Item("IndirectProtectedFields", "Protected Fields Demo", - "demo exception when component class contains protected fields"), - - new Item("injectcomponentdemo", "Inject Component Demo", "inject component defined in template"), - - new Item("cachedpage", "Cached Annotation", "Caching method return values"), - - new Item("cachedpage2", "Cached Annotation2", "Caching method return values w/ inheritence"), - - new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax"), - - new Item("methodadvicedemo", "Method Advice Demo", "Advising component methods."), - - new Item("HasBodyDemo", "Has Body Demo", "Verify the hasBody() method of ComponentResources"), - - new Item("BeanEditorBeanEditContext", "BeanEditor BeanEditContext", - "BeanEditContext is pushed into enviroment by BeanEditor."), - - new Item("InformalParametersDemo", "Informal Parameters Demo", - "Access to informal parameters names and values"), - - new Item("FormFieldOutsideForm", "Form Field Outside Form", - "Nice exception message for common problem of form fields outside forms"), - - new Item("SubmitWithContext", "Submit With Context", "Providing a context for Submit component"), - - new Item("MessageConstraintGeneratorDemo", "Validation Constraints From Messages", - "Providing validators to apply from a properties file"), - - new Item("RenderClientIdDemo", "RenderClientId Mixin", - "Force render of client-side id of a client element via the RenderClientId mixin"), - - new Item("BindParameterDemo", "BindParameter mixin annotation", - "Accessing component parameter values from a mixin"), - - new Item("BindParameterNoSuchParameter", "BindParameter error handling", - "BindParameter throws exception if the containing component doesn't have a matching parameter"), - - new Item("BindParameterOnComponent", "BindParameter on component", - "Verify that BindParameter can only be used on mixin fields"), - - new Item("MixinOrderingDemo", "Mixin Ordering Demo", "Various mixin-ordering scenarios"), - - new Item( - "MissingComponentClassException", - "Missing Component Class Exception", - "Meaningful exception message thrown when component class can't be determined from template or field in containing component."), - - new Item("SessionAttributeDemo", "SessionAttribute Demo", - "Annotation to map a field to a specific session attribute"), - - new Item("BeanEditCalendarDemo", "BeanEditor / Calendar Demo", - "Use of calendar properties inside BeanEditor and BeanDisplay"), - - new Item("TriggerDemo", "Trigger Demo", "Use of Trigger component"), - - new Item("ImageSubmitDemo", "Submit with an Image Demo", - "Make sure that submit with the image parameter set triggers the 'selected' event."), - - new Item("SelectZoneDemo", "Select Zone Demo", "Use a Select component to update a zone."), - - new Item("AssetProtectionDemo", "Asset Protection Demo", - "AssetProtectionDispatcher is properly contributed and functioning"), - - new Item("BeanDisplayEnumDemo", "BeanDisplay Enum Demo", - "User represenation of enum values is correctly read from messages"), - - new Item("unavailablecomponentdemo", "Report Location of Unavailable Component", - "Report Location of Unavailable Component"), - - new Item("discardafterdemo", "@DiscardAfter Demo", "Demo using @DiscardAfter annotation"), - - new Item("SelectDemo", "Select Demo", "Validation decoration for Select"), - - new Item("SelectModelFromObjectsAndPropertyNameDemo", "SelectModel from objects and property name", - "Creating a SelectModel from a list of objects and a label property name"), - - new Item("SelectModelFromObjectsDemo", "SelectModel from objects", - "Creating a SelectModel from a list of objects"), - - new Item("SelectModelCoercionDemo", "SelectModel coercion", - "Creating a SelectModel from a list of objects using coercion"), - - new Item("DecoratePageRenderLinkDemo", "Decorate Page Render Link Demo", - "Decorating page render links"), - - new Item("DecorateComponentEventLinkDemo", "Decorate Component Event Link Demo", - "Decorating event links"), - - new Item("ValidatorMacroDemo", "Validator Macro Demo", "Using validator macros"), - - new Item("AtInjectDemo", "@jakarta.inject.Inject Demo", "Using @jakarta.inject.Inject for injection"), - - new Item("LinkQueryParameters", "Link Query Parameters Demo", - "Providing Query Parameters directly to link components as a map of key=parameter name, value=parameter values"), - - new Item("ChecklistDemo", "Checklist Demo", "Use Checklist component"), - - new Item("BeanEditFormPrepareBubbling", "BeanEditor Prepare Bubbling Demo", "Prepare event bubbling"), - - new Item("NestedFormFragment", "Nested Form Fragment Demo", "Nesting Form Fragments work properly"), - - new Item("MapExpressionInExpansions", "Map Expressions in Expansions Demo", "Maps can be used in expansions"), - - new Item("ExpressionInJsFunction", "Expressions in JS Functions Demo", "Expressions can be used inside javascript functions"), - - new Item("FormFieldFocusDemo", "FormFieldFocus (DEPRECATED) Demo", "Setting the Form focus on a specific field"), - - new Item("FormFragmentExplicitVisibleBoundsDemo", "Form Fragment Explicit Visible Bounds Demo", "Check for form fragment parent visibility can be bounded to"), - - new Item("OverrideFieldFocusDemo", "OverrideFieldFocus Demo", "Setting the focus in a form to a specific field"), - - new Item("OverrideLabelClassDemo", "Override Label Class Demo", "Setting class attribute on Label component"), - - new Item("FormLinkParameters", "FormLinkParameters Demo", "Form link parameters should be unescaped for a hidden field"), - - new Item("KnownActivationContextDemo", "Known Activation Context Demo", "Page is displayed normally if called without context (TAP5-2070)", - "Exact"), - - new Item("UnknownActivationContextDemo", "Unknown Activation Context Demo", "Page refuse to serve if called with an unknown activation context (TAP5-2070)", - "Unwanted", "context"), - - new Item("ModuleConfigurationCallbackDemo", "ModuleConfigurationCallback Demo", "Shows an example of changing the Require.js configuration using JavaScriptSupport.addModuleConfigurationDemo()"), - - new Item("PartialTemplateRendererDemo", "PartialTemplateRenderer Demo", "Shows some examples of rendering blocks and components to a String using PartialTemplateRenderer"), - - new Item("nested/PageThatThrowsException", "Reload on nested page", "Tests a page reload from a nested page's exception report"), - - new Item("inplacegridinloopdemo", "In-Place Grid in a Loop Demo", "In-place grid in a loop"), - - new Item("GenericTypeDemo", "Generic bound type demo", "Tests that generic type info is available for generic bindings"), - - new Item("FormFieldClientIdParameterDemo", "Form Field clientId Parameter Demo", "Shows and tests how to explicitly set the id of a form field component"), - - new Item("gridwithsubmitwithcontextdemo", "Grid with Submit with context", "A grid whose rows contain a Submit component with context"), - - new Item("textfieldwithnullvalidateparameter", "TextField with null validate parameter", "A TextField whose validate parameter is bound to null"), - - new Item("validateCheckboxMustBeChecked", "Validate Checkbox Must Be Checked", "A form that trigger validate in " + - "error event on submit when checkbox is not checked"), - - new Item("validateCheckboxMustBeUnchecked", "Validate Checkbox Must Be Unchecked", "A form that trigger validate in " + - "error event on submit when checkbox is checked"), - - new Item("validateInErrorEvent", "Validate in error Event", "A form that trigger validate in " + - "error event on submit when textfield is empty"), - - new Item("onactivateredirect", "OnActivateRedirect Demo", "A page that redirects to itself from" - + " its activation method"), - - new Item("BeanEditorWithFormFragmentDemo", "Bean Editor With Form Fragment Demo", "TriggerFragment mixin used inside a BeanEditor"), - - new Item("ObjectEditorDemo","Object Editor Demo","Edit Bean with address objects"), - - new Item("IfDemo","If Demo","If component with all its options"), - - new Item("RecursiveDemo","Recursive Demo","Recursive component example"), - - new Item("SelfRecursiveDemo", "Self-Recursive Demo", "check for handling of self-recursive components"), - - new Item("EsModuleDemo", "ES Module Demo", "tests and demonstrations for the ES module support") +// new Item("PublishEventDemo", "@PublishEvent Demo", "Publishing server-side events to client-side code (JavaScript)"), +// +// new Item("StaticActivationContextValueDemo", "@StaticActivationContextValue Demo", "Demonstrates the usage of @StaticActivationContextValue"), +// +// new Item("rest/RestWithOnEventDemo", "REST with @OnEvent Demo", "Demonstrates the usage of @OnEvent to handle REST requests"), +// +// new Item("rest/RestWithEventHandlerMethodNameDemo", "REST with Event Handler Method Name Demo", "Demonstrates the usage of event handler method names to handle REST requests"), +// +// new Item("Html5DateFieldDemo", "Html5DateField Demo", "Choosing dates using the native HTML5 date picker"), +// +//// new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."), +// +// new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."), +// +// new Item("OverrideEventHandlerDemo", "Event Handler Override Demo", "Event Handler methods overridden by sub-classes invoke base-class correctly."), +// +// new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."), +// +// new Item("MissingRequiredARP", "Missing Query Parameter for @ActivationRequestParameter", "Activating a page with a required @ActivationRequestParameter, but no matching query parameter, is an error."), +// +//// new Item("DateFieldValidationDemo", "DateField Validation Demo", +//// "Use of DateField component when client validation is disabled."), +// +// new Item("MixinParameters54", "Strict Mixin Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the mixin id."), +// +// new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."), +// +// new Item("FormCancelActionDemo", "Form Cancel Action Demo", "FormSupport.addCancel() support"), +// +// new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio components inside an Ajax form"), +// +// new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"), +// +// new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"), +// +// new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."), +// +// new Item("MissingAssetDemo", "Missing Asset Demo", "Error when injecting an asset that does not exist."), +// +// new Item("ConfirmDemo", "Confirm Mixin Demo", "Confirm an action when clicking it."), +// +// new Item("SingleErrorDemo", "Single Error", "Using Error component to customize where the errors for a field will be displayed."), +// +// new Item("JavaScriptTests", "JavaScript Tests", "Client-side tests using Mocha and Chai"), +// +// new Item("ModuleInitDemo", "Module-based Initialization Demo", "Invoke a module function to perform page initialization"), +// +// new Item("OperationWorkerDemo", "Operation Worker Demo", "Demonstrate use of @Operation annotation on component methods"), +// +// new Item("MixinParameterDefault", "Mixin Parameter with Default", "Ensure that a mixin parameter with a default value is not reported as unbound."), +// +// new Item("MixinVsInformalParameter", "Mixin Parameter vs. Informal Parameter", "Informal Paramters vs. Mixin parameter of same name"), +// +// new Item("inherit/childa", "TAP5-1656 Demo", "Test a reported bug in component inheritance"), +// +// new Item("ComponentInsideBlockDemo", "Component Inside Block Demo", "Verify that a component, inside a block, is still an embedded "), +// +// new Item("EventMethodUnmatchedComponentId", "Unmatched Component Id in Event Method Demo", "Show that referencing a component that does not exist in an event handler method name is an error."), +// +// new Item("AlertsDemo", "Alerts Demo", "Managing alerts both traditional and Ajax"), +// +// new Item("ClientConsoleDemo", "Client Console Demo", "Demo for the JavaScript client-side console"), +// +// new Item("InvalidFormalParameterDemo", "Unmatched Formal Parameter with @Component", "Parameters specified with @Component annotation must match formal parameters"), +// +// new Item("NullBindingToPrimitive", "Null Bound to Primitive Demo", "Correct exception when a primitive parameter is bound to null"), +// +// new Item("TreeDemo", "Tree Component Demo", "Demo of Tree Component"), +// +// new Item("TreeSelectionDemo", "Tree Component Selection Demo", "Demo of Selection with Tree Component"), +// +// new Item("TreeNoRootsDemo", "Tree Component No Roots Demo", "Demo of No Roots with Tree Component"), +// +// new Item("InvalidExpressionInDynamicTemplate", "Invalid Dynamic Expression", +// "Invalid expression in a Dynamic Template"), +// +// new Item("DynamicDemo", "Dynamic Demo", "Basic Dynamic component tests"), +// +// new Item("DynamicExpansionsDemo", "Expansions in Dynamic Templates", +// "Expansions inside Dynamic component content and attributes"), +// +// new Item("PACAnnotationDemo", "PageActivationContext Demo", +// "Shows that @PageActivationContext fields are set before calls to the activate event handler."), +// +// new Item("PACMultipleAnnotationDemo", "PageActivationContext Multiple Demo", +// "Demonstrates multiple @PageActivationContext fields."), +// +// new Item("PublicFieldAccessDemo", "Public Field Access Demo", "Demonstrates TAP5-1222 fix"), +// +// new Item("ActivationRequestParameterDemo", "ActivationRequestParameter Annotation Demo", +// "Use of @ActivationRequestParameter to encode page state into query parameters"), +// +// new Item("LibraryMessagesDemo", "Library Messages Demo", +// "Demo ability to contribute additional message catalog resources to the application global catalog."), +// +// new Item("MultiZoneUpdateInsideForm", "MultiZone Update inside a Form", +// "Update multiple zones within a single Form."), +// +// new Item("ZoneFormUpdateDemo", "Zone/Form Update Demo", "Updating a Zone inside a Form"), +// +// new Item("MultiZoneStringBodyDemo", "MultiZone String Body Demo", +// "Multi-zone updates in a loop using strings coerced into blocks"), +// +// new Item("RenderNotificationDemo", "RenderNotification Demo", "Use of RenderNotification mixin"), +// +// new Item("InjectMessagesDemo", "Inject Global Messages into Service Demo", +// "Ensure that it is possible to inject the application global message catalog into a service"), +// +// new Item("ReloadDemo", "Reloadable Service Implementation Demo", +// "Used when manually testing service reloads"), +// +// new Item("RequestParameterDemo", "RequestParameter Annotation Demo", +// "Use of @RequestParameter annotation on event handler method parameters"), +// +// new Item("CancelDemo", "Cancel Demo", "Use of the cancel option with Submit"), +// +// new Item("CanceledEventDemo", "Canceled Event Demo", "Triggering of the canceled event from a form."), +// +// new Item("PageResetDemo", "PageReset Annotation Demo", +// "Use of PageReset annotation to re-initialize page state"), +// +// new Item("TestOnlyServiceDemo", "Test Only Service Demo", +// "IoC module available via web.xml configuration"), +// +// new Item("RenderObjectExceptionDemo", "RenderObject Exception Demo", +// "Demonstrate how exceptions when rendering default objects are displayed."), +// +// new Item("MultiLevelInheritDemo", "Multi-Level Inherit Demo", +// "Use of inherit: binding prefix across three levels"), +// +// new Item("HiddenDemo", "Hidden Demo", "Demo the use of the Hidden component."), +// +// new Item("FormZoneDemo", "Form Zone Demo", "Use a form to update a zone."), +// +// new Item("ZoneUpdateNamespace", "Zone/Namespace Interaction", "Prove that TAP5-573 is fixed"), +// +// new Item("AbstractComponentDemo", "Abstract Component Demo", "Error when a component is abstract"), +// +// new Item("TemplateOverrideDemo", "Template Override Demo", +// "Child component extends and overrides parent template."), +// +// new Item("MultiZoneUpdateDemo", "Multiple Zone Update Demo", +// "A single request can now update multiple Zones"), +// +// new Item("LinkSubmitInZoneDemo", "LinkSubmit inside Zone", +// "Ensure that a LinkSubmit works correctly when its containing Form updates a Zone"), +// +// new Item("ProgressiveDemo", "ProgressiveDisplay Demo", "Progressive Enhancement via a component"), +// +// new Item("ClientNumericValidationDemo", "Client-Side Numeric Validation", +// "Client-side locale-specific validation"), +// +// new Item("PublishParametersDemo", "Publish Parameters Demo", +// "Use of @Component.publishParameters attribute."), +// +// new Item("LinkSubmitDemo", "LinkSubmit Demo", "JavaScript LinkSubmit component"), +// +// new Item("LinkSubmitWithoutValidatorDemo", "LinkSubmit Without Validator Demo", +// "Demonstrates that the LinkSubmit component is working without a validator on any of fields in the form"), +// +// new Item("PerFormValidationMessageDemo", "Per-Form Validation Messages", +// "Per-form configuration of validation messages and constraints."), +// +// new Item("EmptyLoopDemo", "Empty Loop Demo", "Use of empty parameter with the Loop component."), +// +// new Item("GenericLoopDemo", "Generic Loop Demo", +// "Use of generic parameters with the Loop component."), +// +// new Item("LoopWithMixinDemo", "Loop With Mixin Demo", +// "Use a mixin with a Loop component."), +// +// new Item("BlankPasswordDemo", "Blank Password Demo", +// "Show that a blank value in a PasswordField does not update the server side value."), +// +// new Item("GridFormEncoderDemo", "Grid Form Encoder Demo", +// "Grid inside a Form using the ValueEncoder option"), +// +// new Item("GridFormWithInitialSortMixinDemo", "Grid Form With Initial Sort Mixin Demo", +// "Grid inside a Form using the InitialSort mixin"), +// +// new Item("DateFieldAjaxFormLoop", "DateField inside AjaxFormLoop", +// "Show that DateField component works correctly inside AjaxFormLoop"), +// +// new Item("NestedForm", "Nested Form Demo", "Error when a Form is nested inside another Form."), +// +// new Item("UnhandledEventDemo", "Unhandled Event Demo", +// "Events that don't have matching event handlers cause exceptions"), +// +// new Item("PrimitiveDefaultDemo", "Primitive Default Demo", +// "Primitive value returned from parameter default method"), +// +// new Item("ValidateFormValidationExceptionDemo", "ValidationForm ValidationException Demo", +// "Throwing a ValidationException from the validateForm event handler."), +// +// new Item("ClientFormatDemo", "Client Format Validation", "Client-side input format validation"), +// +// new Item("ShortGrid", "Short Grid", +// "Grid where the number of claimed rows is less than the number of actual rows"), +// +// new Item("NullParameterDemo", "Null Parameter Demo", "Binding a not-null parameter to null."), +// +// new Item("nestedbeaneditor", "Nested BeanEditor", +// "BeanEditor as override for property editor in BeanEditForm"), +// +// new Item("actionpage", "Action Page", "tests fixture for ActionLink component"), +// +// new Item("cleancachedemo", "Clean Cache Demo", "cache cleared properly during Ajax calls"), +// +// new Item("numberbeaneditordemo", "Number BeanEditor Demo", +// "use of nulls and wrapper types with BeanEditor"), +// +// new Item("forminjectordemo", "FormInjector Demo", "extending a form dynamically via Ajax"), +// +// new Item("music", "Music Page", "demo handling of edge cases of page naming"), +// +// new Item("PersistentDemo", "Persistent Demo", "storing and clearing persistent properties"), +// +// new Item("ActionViaLinkDemo", "Action via Link Demo", "tests creating an action link explicitly"), +// +// new Item("FormFragmentDemo", "Form Fragment Demo", "page with dynamic form sections"), +// +// new Item("BooleanDemo", "Boolean Property Demo", +// "demo boolean properties using both is and get prefixes"), +// +// new Item("DeleteFromGridDemo", "Delete From Grid", "demo deleting items form a Grid"), +// +// new Item("RenderErrorDemo", "Render Error Demo", "reporting of errors while rendering"), +// +// new Item("nested/AssetDemo", "AssetDemo", "declaring an image using Assets"), +// +// new Item("nested/ActionDemo", "Action With Context Demo", +// "using action links with context on page with activation context"), +// +// new Item("blockdemo", "BlockDemo", "use of blocks to control rendering"), +// +// new Item("countdown", "Countdown Page", "defining component using @Component annotation"), +// +// new Item("injectdemo", "Inject Demo", "use of various kinds of injection"), +// +// new Item("instancemixin", "InstanceMixin", "mixin added to a particular component instance"), +// +// new Item("TextFieldWrapperTypeDemo", "TextField Wrapper Types", +// "use of TextField to edit numeric wrapper types (not primitives) "), +// +// new Item("EnvironmentalDemo", "Environmental Annotation Usage", +// "Storing and retrieving Environmental values"), +// +// new Item("Expansion", "Expansion Page", "Use of expansions in templates"), +// +// new Item("ExpansionSubclass", "ExpansionSubclass", +// "components can inherit templates from base classes"), +// +// new Item("Localization", "Localization", "access localized messages from the component catalog"), +// +// new Item("NumberSelect", "NumberSelect", "passivate/activate page context demo"), +// +// new Item("ParameterConflict", "Template Overridden by Class Page", +// "Parameters in the class override those in the template"), +// +// new Item("ParameterDefault", "ParameterDefault", "defaulter methods for component parameters"), +// +// new Item("passwordfielddemo", "PasswordFieldDemo", "test for the PasswordField component"), +// +// new Item("rendercomponentdemo", "RenderComponentDemo", +// "components that \"nominate\" other components to render"), +// +// new Item("renderphaseorder", "RenderPhaseOrder", +// "order of operations when invoking render phase methods"), +// +// new Item("simpleform", "SimpleForm", "first pass at writing Form and TextField components"), +// +// new Item("OptionGroupForm", "OptionGroupForm Demo", "Select with Option Group"), +// +// new Item("validform", "ValidForm", "server-side input validation"), +// +// new Item("ToDoListVolatile", "ToDo List (Volatile)", "Loops and Submit inside Form, volatile mode"), +// +// new Item("MissingTemplate", "Missing Template Demo", +// "Demo for what happens when a template is not found for a page"), +// +// new Item("nested/zonedemo", "Zone Demo", "dynamic updates within a page"), +// +// new Item("todolist", "ToDo List", "Loops and Submit inside Form using primary key encoder"), +// +// new Item("flashdemo", "FlashDemo", "demonstrate 'flash' persistence"), +// +// new Item("beaneditordemo", "BeanEditor Demo", "demonstrate the BeanEditor mega-component"), +// +// new Item("pageloadeddemo", "PageLoaded Demo", "shows that page lifecycle methods are invoked"), +// +// new Item("griddemo", "Grid Demo", "default Grid component"), +// +// new Item("GridInLoopDemo", "Grid In Loop Demo", "Grid inside loop with different model on each iteration"), +// +// new Item("nullgrid", "Null Grid", "handling of null source for Grid"), +// +// new Item("gridsetdemo", "Grid Set Demo", "handling of Set sources for Grid"), +// +// new Item("gridenumdemo", "Grid Enum Demo", "handling of enum types in the Grid"), +// +// new Item("GridRemoveReorderDemo", "Grid Remove/Reorder Demo", +// "handling of remove and reorder parameters"), +// +// new Item("EmptyGrid", "Empty Grid Demo", "show table for empty data sources"), +// +// new Item("GridEarlyPagingDemo", "Grid Early Paging", "set a Grid's current page before rendering"), +// +// new Item("protected", "Protected Page", +// "Demonstrate result of non-void return from a page's activate method"), +// +// new Item("Kicker", "Kicker", "demos complex page and component context in links"), +// +// new Item("simpletrackgriddemo", "SimpleTrack Grid Demo", +// "customizing the model for a Grid around an interface"), +// +// new Item("pagelinkcontext", "PageLink Context Demo", +// "passing explicit context in a page render link"), +// +// new Item("pagecontextinform", "Page Context in Form", "passivate/activate page context in Form", +// "betty", "wilma", "context with spaces", "context/with/slashes"), +// +// new Item("ValidBeanEditorDemo", "Client Validation Demo", "BeanEditor with validation enabled"), +// +// new Item("Unreachable", "Unreachable Page", "page not reachable due to IgnoredPathsFilter"), +// +// new Item("renderabledemo", "Renderable Demo", +// "shows that render phase methods can return a Renderable"), +// +// new Item("inheritedbindingsdemo", "Inherited Bindings Demo", +// "Tests for components that inherit bindings from containing components"), +// +// new Item("ClientPersistenceDemo", "Client Persistence Demo", +// "component field values persisted on the client side"), +// +// new Item("attributeExpansionsDemo", "Attribute Expansions Demo", +// "use expansions inside attributes of ordinary elements"), +// +// new Item("PaletteDemo", "Palette Demo", "multiple selection component"), +// new Item("PaletteGroupedDemo", "Palette Grouped Demo", "multiple selection component (grouped)"), +// +// new Item("ReturnTypes", "Return Types", "tests various event handler return types"), +// +// new Item("FormEncodingType", "Form Encoding Type", +// "Test ability to set an encoding type for a Form"), +// +// new Item("RadioDemo", "RadioDemo", "Use of the RadioGroup and Radio components"), +// +// new Item("RegexpDemo", "Regexp Demo", "Use of the Regexp validator"), +// +// new Item("BeanEditRemoveReorder", "BeanEdit Remove/Reorder", +// "Use of the remove and reorder parameters with BeanEditForm"), +// +// new Item("MultiBeanEditDemo", "MultiBeanEdit Demo", +// "Multiple BeanEditor components in a single form"), +// +// new Item("GridFormDemo", "Grid Form Demo", "Grid operating inside a Form"), +// +// new Item("DateFieldDemo", "DateField Demo", "using DateField by itself on a page"), +// +// new Item("BeanEditDateDemo", "BeanEditor / Date Demo", +// "Use of date properties inside BeanEditor and BeanDisplay"), +// +// new Item("eventmethodtranslate", "EventMethod Translator", +// "Demo ability to provide toclient and parseclient event handler methods"), +// +// new Item("autocompletedemo", "Autocomplete Mixin Demo", +// "Demo the autocomplete mixin for text fields"), +// +// new Item("componentparameter", "ComponentParameter Demo", +// " Demo using a component type as a parameter type and succesfully passing a component"), +// +// new Item("inheritinformalsdemo", "Inherit Informal Parameters Demo", +// "Demo a component which inherits informal parameters from its container"), +// +// new Item("disabledfields", "Disabled Fields", +// "Demonstrate a bunch of disabled fields, to verify that the RenderDisabled mixin works and is being used properly"), +// +// new Item("BeanEditorOverride", "BeanEditor Override", +// "Property editor overrides work for the BeanEditor component itself (not just the BeanEditForm component)"), +// +// new Item("varbindingdemo", "Var Binding Demo", "use of the var: binding prefix"), +// +// new Item("leangriddemo", "Lean Grid Demo", +// "Grid component with lean parameter turned on, to eliminate CSS class attributes in TD and TH elements"), +// +// new Item("blockcaller", "Action Links off of Active Page", +// "Actions can exist on pages other than the active page, via Blocks."), +// +// new Item("unlessdemo", "Unless Demo", "use of the Unless component"), +// +// new Item("delegateinline", "Inline Delegate", +// "Using the delegate component to create inline components"), +// +// new Item("MagicValueEncoder", "Magic ValueEncoder Demo", +// "Automatic creation of ValueEncoder using the TypeCoercer"), +// +// new Item("NullStrategyDemo", "Null Field Strategy Demo", "use of the nulls parameter of TextField"), +// +// new Item("OverrideValidationDecorator", "Override Validation Decorator", +// "override the default validation decorator"), +// +// new Item("ExceptionEventDemo", "Exception Event Demo", "handling component event exceptions"), +// +// new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"), +// +// new Item("PrimitiveArrayParameterDemo", "Primitive Array Parameter Demo", +// "use primitive array as parameter type"), +// +// new Item("RenderPhaseMethodExceptionDemo", "Render Phase Method Exception Demo", +// "render phase methods may throw checked exceptions"), +// +// new Item("TrackEditor", "Generic Page Class Demo", +// "demo use of generics with component classes and, particularily, with property types"), +// +// new Item("IndirectProtectedFields", "Protected Fields Demo", +// "demo exception when component class contains protected fields"), +// +// new Item("injectcomponentdemo", "Inject Component Demo", "inject component defined in template"), +// +// new Item("cachedpage", "Cached Annotation", "Caching method return values"), +// +// new Item("cachedpage2", "Cached Annotation2", "Caching method return values w/ inheritence"), +// +// new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax"), +// +// new Item("methodadvicedemo", "Method Advice Demo", "Advising component methods."), +// +// new Item("HasBodyDemo", "Has Body Demo", "Verify the hasBody() method of ComponentResources"), +// +// new Item("BeanEditorBeanEditContext", "BeanEditor BeanEditContext", +// "BeanEditContext is pushed into enviroment by BeanEditor."), +// +// new Item("InformalParametersDemo", "Informal Parameters Demo", +// "Access to informal parameters names and values"), +// +// new Item("FormFieldOutsideForm", "Form Field Outside Form", +// "Nice exception message for common problem of form fields outside forms"), +// +// new Item("SubmitWithContext", "Submit With Context", "Providing a context for Submit component"), +// +// new Item("MessageConstraintGeneratorDemo", "Validation Constraints From Messages", +// "Providing validators to apply from a properties file"), +// +// new Item("RenderClientIdDemo", "RenderClientId Mixin", +// "Force render of client-side id of a client element via the RenderClientId mixin"), +// +// new Item("BindParameterDemo", "BindParameter mixin annotation", +// "Accessing component parameter values from a mixin"), +// +// new Item("BindParameterNoSuchParameter", "BindParameter error handling", +// "BindParameter throws exception if the containing component doesn't have a matching parameter"), +// +// new Item("BindParameterOnComponent", "BindParameter on component", +// "Verify that BindParameter can only be used on mixin fields"), +// +// new Item("MixinOrderingDemo", "Mixin Ordering Demo", "Various mixin-ordering scenarios"), +// +// new Item( +// "MissingComponentClassException", +// "Missing Component Class Exception", +// "Meaningful exception message thrown when component class can't be determined from template or field in containing component."), +// +// new Item("SessionAttributeDemo", "SessionAttribute Demo", +// "Annotation to map a field to a specific session attribute"), +// +// new Item("BeanEditCalendarDemo", "BeanEditor / Calendar Demo", +// "Use of calendar properties inside BeanEditor and BeanDisplay"), +// +// new Item("TriggerDemo", "Trigger Demo", "Use of Trigger component"), +// +// new Item("ImageSubmitDemo", "Submit with an Image Demo", +// "Make sure that submit with the image parameter set triggers the 'selected' event."), +// +// new Item("SelectZoneDemo", "Select Zone Demo", "Use a Select component to update a zone."), +// +// new Item("AssetProtectionDemo", "Asset Protection Demo", +// "AssetProtectionDispatcher is properly contributed and functioning"), +// +// new Item("BeanDisplayEnumDemo", "BeanDisplay Enum Demo", +// "User represenation of enum values is correctly read from messages"), +// +// new Item("unavailablecomponentdemo", "Report Location of Unavailable Component", +// "Report Location of Unavailable Component"), +// +// new Item("discardafterdemo", "@DiscardAfter Demo", "Demo using @DiscardAfter annotation"), +// +// new Item("SelectDemo", "Select Demo", "Validation decoration for Select"), +// +// new Item("SelectModelFromObjectsAndPropertyNameDemo", "SelectModel from objects and property name", +// "Creating a SelectModel from a list of objects and a label property name"), +// +// new Item("SelectModelFromObjectsDemo", "SelectModel from objects", +// "Creating a SelectModel from a list of objects"), +// +// new Item("SelectModelCoercionDemo", "SelectModel coercion", +// "Creating a SelectModel from a list of objects using coercion"), +// +// new Item("DecoratePageRenderLinkDemo", "Decorate Page Render Link Demo", +// "Decorating page render links"), +// +// new Item("DecorateComponentEventLinkDemo", "Decorate Component Event Link Demo", +// "Decorating event links"), +// +// new Item("ValidatorMacroDemo", "Validator Macro Demo", "Using validator macros"), +// +// new Item("AtInjectDemo", "@jakarta.inject.Inject Demo", "Using @jakarta.inject.Inject for injection"), +// +// new Item("LinkQueryParameters", "Link Query Parameters Demo", +// "Providing Query Parameters directly to link components as a map of key=parameter name, value=parameter values"), +// +// new Item("ChecklistDemo", "Checklist Demo", "Use Checklist component"), +// +// new Item("BeanEditFormPrepareBubbling", "BeanEditor Prepare Bubbling Demo", "Prepare event bubbling"), +// +// new Item("NestedFormFragment", "Nested Form Fragment Demo", "Nesting Form Fragments work properly"), +// +// new Item("MapExpressionInExpansions", "Map Expressions in Expansions Demo", "Maps can be used in expansions"), +// +// new Item("ExpressionInJsFunction", "Expressions in JS Functions Demo", "Expressions can be used inside javascript functions"), +// +// new Item("FormFieldFocusDemo", "FormFieldFocus (DEPRECATED) Demo", "Setting the Form focus on a specific field"), +// +// new Item("FormFragmentExplicitVisibleBoundsDemo", "Form Fragment Explicit Visible Bounds Demo", "Check for form fragment parent visibility can be bounded to"), +// +// new Item("OverrideFieldFocusDemo", "OverrideFieldFocus Demo", "Setting the focus in a form to a specific field"), +// +// new Item("OverrideLabelClassDemo", "Override Label Class Demo", "Setting class attribute on Label component"), +// +// new Item("FormLinkParameters", "FormLinkParameters Demo", "Form link parameters should be unescaped for a hidden field"), +// +// new Item("KnownActivationContextDemo", "Known Activation Context Demo", "Page is displayed normally if called without context (TAP5-2070)", +// "Exact"), +// +// new Item("UnknownActivationContextDemo", "Unknown Activation Context Demo", "Page refuse to serve if called with an unknown activation context (TAP5-2070)", +// "Unwanted", "context"), +// +// new Item("ModuleConfigurationCallbackDemo", "ModuleConfigurationCallback Demo", "Shows an example of changing the Require.js configuration using JavaScriptSupport.addModuleConfigurationDemo()"), +// +// new Item("PartialTemplateRendererDemo", "PartialTemplateRenderer Demo", "Shows some examples of rendering blocks and components to a String using PartialTemplateRenderer"), +// +// new Item("nested/PageThatThrowsException", "Reload on nested page", "Tests a page reload from a nested page's exception report"), +// +// new Item("inplacegridinloopdemo", "In-Place Grid in a Loop Demo", "In-place grid in a loop"), +// +// new Item("GenericTypeDemo", "Generic bound type demo", "Tests that generic type info is available for generic bindings"), +// +// new Item("FormFieldClientIdParameterDemo", "Form Field clientId Parameter Demo", "Shows and tests how to explicitly set the id of a form field component"), +// +// new Item("gridwithsubmitwithcontextdemo", "Grid with Submit with context", "A grid whose rows contain a Submit component with context"), +// +// new Item("textfieldwithnullvalidateparameter", "TextField with null validate parameter", "A TextField whose validate parameter is bound to null"), +// +// new Item("validateCheckboxMustBeChecked", "Validate Checkbox Must Be Checked", "A form that trigger validate in " + +// "error event on submit when checkbox is not checked"), +// +// new Item("validateCheckboxMustBeUnchecked", "Validate Checkbox Must Be Unchecked", "A form that trigger validate in " + +// "error event on submit when checkbox is checked"), +// +// new Item("validateInErrorEvent", "Validate in error Event", "A form that trigger validate in " + +// "error event on submit when textfield is empty"), +// +// new Item("onactivateredirect", "OnActivateRedirect Demo", "A page that redirects to itself from" +// + " its activation method"), +// +// new Item("BeanEditorWithFormFragmentDemo", "Bean Editor With Form Fragment Demo", "TriggerFragment mixin used inside a BeanEditor"), +// +// new Item("ObjectEditorDemo","Object Editor Demo","Edit Bean with address objects"), +// +// new Item("IfDemo","If Demo","If component with all its options"), +// +// new Item("RecursiveDemo","Recursive Demo","Recursive component example"), +// +// new Item("SelfRecursiveDemo", "Self-Recursive Demo", "check for handling of self-recursive components"), +// +// new Item("EsModuleDemo", "ES Module Demo", "tests and demonstrations for the ES module support") ); static From ead827a0425863649f03697fbfffc6bb84ee9940 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Sat, 26 Jul 2025 12:24:23 -0300 Subject: [PATCH 04/30] TAP5-2810: More needed support code for Require.js-less mode --- 5_10_RELEASE_NOTES.md | 11 +- .../tapestry5/corelib/components/Zone.java | 8 +- .../internal/services/DocumentLinkerImpl.java | 29 +- .../services/ajax/BaseInitialization.java | 10 + .../ajax/EsModuleInitializationImpl.java | 12 +- .../services/ajax/EsShimManagerImpl.java | 66 + .../services/ajax/JavaScriptSupportImpl.java | 27 +- .../javascript/EsModuleManagerImpl.java | 58 +- .../services/javascript/EsShimDispatcher.java | 31 +- .../tapestry5/modules/JavaScriptModule.java | 189 ++- .../tapestry5/modules/TapestryModule.java | 7 +- .../services/javascript/EsModuleManager.java | 14 +- .../tapestry5/services/javascript/EsShim.java | 91 +- .../services/javascript/EsShimManager.java | 49 + .../javascript/ExtensibleJavaScriptStack.java | 18 +- .../services/javascript/JavaScriptStack.java | 8 + .../services/javascript/StackExtension.java | 10 + .../javascript/StackExtensionType.java | 7 + .../assets/es-modules/t5/underscore.js | 5 - .../src/t5/core/t53-compatibility.ts | 56 + .../integration/app1/components/Border.java | 14 +- .../integration/app1/pages/Index.java | 1150 ++++++++--------- .../assets/es-modules/app/test-support.js | 14 + 23 files changed, 1118 insertions(+), 766 deletions(-) create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsShimManagerImpl.java create mode 100644 tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShimManager.java delete mode 100644 tapestry-core/src/main/resources/META-INF/assets/es-modules/t5/underscore.js create mode 100644 tapestry-core/src/main/typescript/src/t5/core/t53-compatibility.ts create mode 100644 tapestry-core/src/test/resources/META-INF/assets/es-modules/app/test-support.js diff --git a/5_10_RELEASE_NOTES.md b/5_10_RELEASE_NOTES.md index b893fdec38..4a942b709a 100644 --- a/5_10_RELEASE_NOTES.md +++ b/5_10_RELEASE_NOTES.md @@ -31,13 +31,10 @@ Scratch pad for changes destined for the 5.10.0 release notes page. # Notes about Require.js disabled mode -* When using Bootstrap 3, the `t5/bootstrap/*` modules had automatic dependency - management (for example, if you `bootstrap/tooltip`, `bootstrap/transition` - would automatically be included too through Require.js). This doesn't happen - when Require.js is disabled. So, for example, when importing `bootstrap/tooltip`, - you should import `bootstrap/trasition` first. Notice Bootstrap 3 JavaScript - files don't have any module management code on it (Require.js nor ES modules) - +* Underscore.js, jQuery and Require.js are not included in the default stack + (i.e. the set of JavaScript files which are included in pages by default). + If you need to use Underscore.js or jQuery, they're automatically available for + import as `underscore` and `jquery`, respectively. # Overall notes diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java index b4e9b02829..7bfd9b7202 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Zone.java @@ -20,6 +20,7 @@ import org.apache.tapestry5.corelib.internal.HiddenFieldPositioner; import org.apache.tapestry5.dom.Element; import org.apache.tapestry5.internal.services.RequestConstants; +import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.ioc.annotations.Inject; import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.json.JSONObject; @@ -68,7 +69,6 @@ * @see FormFragment */ @SupportsInformalParameters -@Import(module = "t5/core/zone") public class Zone implements ClientBodyElement { /** @@ -144,6 +144,9 @@ public class Zone implements ClientBodyElement @Inject private HiddenFieldLocationRules rules; + + @Inject + private RequireJsModeHelper requireJsModeHelper; private String clientId; @@ -175,6 +178,9 @@ void pageLoaded() void beginRender(MarkupWriter writer) { + + requireJsModeHelper.importModule("t5/core/zone"); + clientId = resources.isBound("id") ? idParameter : javascriptSupport.allocateClientId(resources); Element e = writer.element(elementName, diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java index 8814e7faf2..fd885ce877 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java @@ -53,6 +53,8 @@ public class DocumentLinkerImpl implements DocumentLinker private final boolean omitGeneratorMetaTag, enablePageloadingMask; private final String tapestryBanner; + + private final boolean requireJsEnabled; // Initially false; set to true when a scriptURL or any kind of initialization is added. private boolean hasScriptsOrInitializations; @@ -66,12 +68,14 @@ public class DocumentLinkerImpl implements DocumentLinker * @param tapestryVersion */ public DocumentLinkerImpl(ModuleManager moduleManager, EsModuleManager esModuleManager, - boolean omitGeneratorMetaTag, boolean enablePageloadingMask, String tapestryVersion) + boolean omitGeneratorMetaTag, boolean enablePageloadingMask, String tapestryVersion, + boolean requireJsEnabled) { this.moduleManager = moduleManager; this.esModuleManager = esModuleManager; this.omitGeneratorMetaTag = omitGeneratorMetaTag; this.enablePageloadingMask = enablePageloadingMask; + this.requireJsEnabled = requireJsEnabled; tapestryBanner = "Apache Tapestry Framework (version " + tapestryVersion + ')'; } @@ -149,8 +153,6 @@ public void updateDocument(Document document) addElementBefore(head, existingMeta, "meta", "name", "generator", "content", tapestryBanner); } - - addScriptElements(root); final List esModuleInits = esModulesinitsManager.getInits(); if (isHtmlRoot && !esModuleInits.isEmpty()) @@ -158,6 +160,8 @@ public void updateDocument(Document document) esModuleManager.writeImportMap(root.find("head"), esModuleConfigurationCallbacks); esModuleManager.writeImports(root, esModuleInits); } + + addScriptElements(root); } @@ -252,8 +256,11 @@ protected void addContentToBody(Element body) script.moveToTop(body); } - - moduleManager.writeConfiguration(body, moduleConfigurationCallbacks); + + if (requireJsEnabled) + { + moduleManager.writeConfiguration(body, moduleConfigurationCallbacks); + } // Write the core libraries, which includes RequireJS: @@ -264,9 +271,15 @@ protected void addContentToBody(Element body) "src", url); } - // Write the initialization at this point. - - moduleManager.writeInitialization(body, libraryURLs, initsManager.getSortedInits()); + if (requireJsEnabled) + { + // Write the initialization at this point. + moduleManager.writeInitialization(body, libraryURLs, initsManager.getSortedInits()); + } + else + { + esModuleManager.writeInitialization(body, libraryURLs); + } } private static Element createTemporaryContainer(Element headElement, String existingElementName, String newElementName) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/BaseInitialization.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/BaseInitialization.java index 589a8850a4..7714dc4614 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/BaseInitialization.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/BaseInitialization.java @@ -1,3 +1,13 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and package org.apache.tapestry5.internal.services.ajax; import org.apache.tapestry5.ioc.internal.util.InternalUtils; diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java index 3b3bfeb4ab..eb4e321370 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java @@ -1,3 +1,13 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and package org.apache.tapestry5.internal.services.ajax; import java.util.Collections; @@ -14,7 +24,7 @@ public class EsModuleInitializationImpl extends BaseInitialization shims; + + private final PathConstructor pathConstructor; + + private final String assetPrefix; + + public EsShimManagerImpl(Map shims, + PathConstructor pathConstructor, + @Symbol(SymbolConstants.ASSET_PATH_PREFIX) + String assetPrefix) + { + super(); + this.shims = shims; + this.assetPrefix = assetPrefix; + this.pathConstructor = pathConstructor; + } + + /** + * Returns the shims as a (module name, module resource) map. + * @return a {@code Map} + */ + @Override + public Map getShims() + { + return shims; + } + + @Override + public String getRequestPrefix(boolean compress) + { + return pathConstructor.constructDispatchPath(assetPrefix + "/" + (compress ? ES_SUBPATH + ".gz" : ES_SUBPATH) + "/"); + } + + @Override + public String getUrl(String moduleName) { + return getRequestPrefix(true) + moduleName + ".js"; + } + +} diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java index a065b4b6ca..db44db6618 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/JavaScriptSupportImpl.java @@ -34,6 +34,7 @@ import org.apache.tapestry5.ioc.util.IdAllocator; import org.apache.tapestry5.json.JSONArray; import org.apache.tapestry5.json.JSONObject; +import org.apache.tapestry5.services.javascript.AbstractInitialization; import org.apache.tapestry5.services.javascript.EsModuleConfigurationCallback; import org.apache.tapestry5.services.javascript.EsModuleInitialization; import org.apache.tapestry5.services.javascript.Initialization; @@ -46,6 +47,8 @@ public class JavaScriptSupportImpl implements JavaScriptSupport { + private static final String PAGEINIT_MODULE_NAME = "t5/core/pageinit"; + private final IdAllocator idAllocator; private final DocumentLinker linker; @@ -133,10 +136,21 @@ public JavaScriptSupportImpl(DocumentLinker linker, JavaScriptStackSource javasc public void commit() { + final String pageInitModuleName = PAGEINIT_MODULE_NAME; // TODO make no Require.js version of this if (focusFieldId != null) { - require("t5/core/pageinit").invoke("focus").with(focusFieldId); + final AbstractInitialization initialization; + + if (requireJsEnabled) + { + initialization = require(pageInitModuleName); + } + else + { + initialization = importEsModule(pageInitModuleName); + } + initialization.invoke("focus").with(focusFieldId); } F.flow(stylesheetLinks).each(new Worker() @@ -224,7 +238,16 @@ public void addScript(InitializationPriority priority, String format, Object... if (partialMode) { - require("t5/core/pageinit").invoke("evalJavaScript").with(newScript); + final AbstractInitialization initialization; + if (requireJsEnabled) + { + initialization = require(PAGEINIT_MODULE_NAME); + } + else + { + initialization = importEsModule(PAGEINIT_MODULE_NAME); + } + initialization.invoke("evalJavaScript").with(newScript); } else { linker.addScript(priority, newScript); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java index a73a2fe642..5cfebf058f 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.apache.tapestry5.Asset; import org.apache.tapestry5.SymbolConstants; @@ -31,15 +32,18 @@ import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.services.ClasspathMatcher; import org.apache.tapestry5.ioc.services.ClasspathScanner; +import org.apache.tapestry5.json.JSONArray; import org.apache.tapestry5.json.JSONCollection; import org.apache.tapestry5.json.JSONLiteral; import org.apache.tapestry5.json.JSONObject; import org.apache.tapestry5.services.AssetSource; +import org.apache.tapestry5.services.Core; import org.apache.tapestry5.services.assets.StreamableResourceSource; import org.apache.tapestry5.services.javascript.EsModuleConfigurationCallback; import org.apache.tapestry5.services.javascript.EsModuleInitialization; import org.apache.tapestry5.services.javascript.EsModuleManager; import org.apache.tapestry5.services.javascript.ImportPlacement; +import org.apache.tapestry5.services.javascript.JavaScriptStack; public class EsModuleManagerImpl implements EsModuleManager { @@ -60,6 +64,8 @@ public class EsModuleManagerImpl implements EsModuleManager private final Set extensions; private final AssetSource assetSource; + + private final List coreStackInits; // Note: ConcurrentHashMap does not support null as a value, alas. We use classpathRoot as a null. private final Map cache = CollectionFactory.newConcurrentMap(); @@ -74,6 +80,8 @@ public class EsModuleManagerImpl implements EsModuleManager private final List globalPerRequestCallbacks; + private final String infraProvider; + public EsModuleManagerImpl( List contributions, AssetSource assetSource, @@ -82,14 +90,24 @@ public EsModuleManagerImpl( boolean compactJSON, @Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE) boolean productionMode, + @Symbol(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER) + String infraProvider, ClasspathScanner classpathScanner, - ResourceChangeTracker resourceChangeTracker) + ResourceChangeTracker resourceChangeTracker, + @Core JavaScriptStack javaScriptStack) { this.compactJSON = compactJSON; this.assetSource = assetSource; this.classpathScanner = classpathScanner; this.productionMode = productionMode; this.resourceChangeTracker = resourceChangeTracker; + this.infraProvider = infraProvider; + + final List coreStackEsModules = javaScriptStack.getEsModules(); + + coreStackInits = coreStackEsModules.stream() + .map(i -> new EsModuleInitializationImpl(i)) + .collect(Collectors.toList()); baseCallbacks = new ArrayList<>(); globalPerRequestCallbacks = new ArrayList<>(); @@ -151,12 +169,22 @@ private void loadBaseModuleList(JSONObject imports) final Set scan = classpathScanner.scan(CLASSPATH_ROOT, matcher); for (String file : scan) { - String id = file.replace(CLASSPATH_ROOT, ""); - id = id.substring(0, id.lastIndexOf('.')); + String moduleName = file.replace(CLASSPATH_ROOT, ""); + moduleName = moduleName.substring(0, moduleName.lastIndexOf('.')); + + if (moduleName.startsWith("t5/core/t5-core-dom-")) + { + // They're treated specially. + continue; + } + else if (moduleName.equals("t5/core/dom")) + { + file = file.replace("t5/core/dom", "t5/core/t5-core-dom-" + infraProvider); + } final Asset asset = assetSource.getClasspathAsset(file); resourceChangeTracker.trackResource(asset.getResource()); - imports.put(id, asset.toClientURL()); + imports.put(moduleName, asset.toClientURL()); } } catch (IOException e) { @@ -192,7 +220,11 @@ public void writeImports(Element root, List inits) String functionName; Object[] arguments; - for (EsModuleInitialization i : inits) + List allInits = new ArrayList<>(coreStackInits.size() + inits.size()); + allInits.addAll(coreStackInits); + allInits.addAll(inits); + + for (EsModuleInitialization i : allInits) { init = (EsModuleInitializationImpl) i; @@ -281,6 +313,22 @@ else if (placement.equals(ImportPlacement.BODY_TOP)) } + @Override + public void writeInitialization(Element body, List libraryURLs) + { + + Element element = body.element("script", "type", "module"); + + element.raw(String.format("import pageinit from \"t5/core/pageinit\";\npageinit(%s, []);", + convert(libraryURLs))); + } + + private String convert(List input) + { + return new JSONArray().putAll(input).toString(compactJSON); + } + + static String convertToJsFunctionParameters(Object[] arguments, boolean compactJSON) { String result; diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java index fb1a24da9c..03ec9874aa 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsShimDispatcher.java @@ -16,7 +16,6 @@ import java.util.HashMap; import java.util.Map; -import org.apache.tapestry5.SymbolConstants; import org.apache.tapestry5.commons.Resource; import org.apache.tapestry5.http.services.Dispatcher; import org.apache.tapestry5.http.services.Request; @@ -26,13 +25,11 @@ import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker; import org.apache.tapestry5.ioc.IOOperation; import org.apache.tapestry5.ioc.OperationTracker; -import org.apache.tapestry5.ioc.annotations.Symbol; -import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration; -import org.apache.tapestry5.services.PathConstructor; import org.apache.tapestry5.services.assets.StreamableResource; import org.apache.tapestry5.services.assets.StreamableResourceProcessing; import org.apache.tapestry5.services.assets.StreamableResourceSource; import org.apache.tapestry5.services.javascript.EsShim; +import org.apache.tapestry5.services.javascript.EsShimManager; import jakarta.servlet.http.HttpServletResponse; @@ -43,12 +40,9 @@ * * @see EsShim */ -@UsesMappedConfiguration(Resource.class) public class EsShimDispatcher implements Dispatcher { - private static final String ES_SUBPATH = "es-shims"; - private final ResourceStreamer streamer; private final OperationTracker tracker; @@ -59,27 +53,26 @@ public class EsShimDispatcher implements Dispatcher private Map shimMap; - public EsShimDispatcher(Map configuration, + public EsShimDispatcher(EsShimManager esShimManager, StreamableResourceSource streamableResourceSource, ResourceChangeTracker resourceChangeTracker, ResourceStreamer streamer, OperationTracker tracker, - PathConstructor pathConstructor, - @Symbol(SymbolConstants.ASSET_PATH_PREFIX) - String assetPrefix, boolean compress) { this.streamer = streamer; this.tracker = tracker; this.compress = compress; - this.shimMap = new HashMap<>(configuration.size()); + + final Map shims = esShimManager.getShims(); + this.shimMap = new HashMap<>(shims.size()); try { - for (String moduleName : configuration.keySet()) + for (String moduleName : shims.keySet()) { shimMap.put(moduleName, streamableResourceSource.getStreamableResource( - configuration.get(moduleName), StreamableResourceProcessing.COMPRESSION_ENABLED, resourceChangeTracker)); + shims.get(moduleName), StreamableResourceProcessing.COMPRESSION_ENABLED, resourceChangeTracker)); } } catch (IOException e) @@ -87,7 +80,7 @@ public EsShimDispatcher(Map configuration, throw new RuntimeException(e); } - requestPrefix = pathConstructor.constructDispatchPath(assetPrefix + "/" + (compress ? ES_SUBPATH + ".gz" : ES_SUBPATH) + "/"); + requestPrefix = esShimManager.getRequestPrefix(compress); } public boolean dispatch(Request request, Response response) throws IOException @@ -110,11 +103,6 @@ public boolean dispatch(Request request, Response response) throws IOException } - public String getUrl(String moduleName) - { - return requestPrefix + moduleName; - } - private boolean handleModuleRequest(String extraPath, Response response) throws IOException { int dotx = extraPath.lastIndexOf('.'); @@ -141,7 +129,8 @@ public Boolean perform() throws IOException if (resource != null) { - return streamer.streamResource(resource, resource.getChecksum(), ResourceStreamer.DEFAULT_OPTIONS); + final String checksum = resource.getChecksum(); + return streamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS); } return false; diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java index 8b6bf81a02..3ea83348c7 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java @@ -14,7 +14,6 @@ import java.util.Locale; -import org.apache.tapestry5.Asset; import org.apache.tapestry5.BooleanHook; import org.apache.tapestry5.MarkupWriter; import org.apache.tapestry5.SymbolConstants; @@ -29,6 +28,7 @@ import org.apache.tapestry5.internal.InternalConstants; import org.apache.tapestry5.internal.services.DocumentLinker; import org.apache.tapestry5.internal.services.ResourceStreamer; +import org.apache.tapestry5.internal.services.ajax.EsShimManagerImpl; import org.apache.tapestry5.internal.services.ajax.JavaScriptSupportImpl; import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelper; import org.apache.tapestry5.internal.services.ajax.RequireJsModeHelperImpl; @@ -50,7 +50,6 @@ import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.services.FactoryDefaults; import org.apache.tapestry5.ioc.services.SymbolProvider; -import org.apache.tapestry5.ioc.services.SymbolSource; import org.apache.tapestry5.ioc.util.IdAllocator; import org.apache.tapestry5.json.JSONObject; import org.apache.tapestry5.services.AssetSource; @@ -64,13 +63,15 @@ import org.apache.tapestry5.services.PartialMarkupRenderer; import org.apache.tapestry5.services.PartialMarkupRendererFilter; import org.apache.tapestry5.services.PathConstructor; +import org.apache.tapestry5.services.assets.StreamableResourceSource; import org.apache.tapestry5.services.compatibility.Compatibility; import org.apache.tapestry5.services.compatibility.Trait; import org.apache.tapestry5.services.javascript.AMDWrapper; -import org.apache.tapestry5.services.javascript.EsShim; import org.apache.tapestry5.services.javascript.EsModuleConfigurationCallback; import org.apache.tapestry5.services.javascript.EsModuleManager; import org.apache.tapestry5.services.javascript.EsModuleManager.EsModuleManagerContribution; +import org.apache.tapestry5.services.javascript.EsShim; +import org.apache.tapestry5.services.javascript.EsShimManager; import org.apache.tapestry5.services.javascript.ExtensibleJavaScriptStack; import org.apache.tapestry5.services.javascript.JavaScriptModuleConfiguration; import org.apache.tapestry5.services.javascript.JavaScriptStack; @@ -108,7 +109,7 @@ public static void bind(ServiceBinder binder) binder.bind(JavaScriptStack.class, ExtensibleJavaScriptStack.class).withMarker(Core.class).withId("CoreJavaScriptStack"); binder.bind(JavaScriptStack.class, ExtensibleJavaScriptStack.class).withMarker(Internal.class).withId("InternalJavaScriptStack"); binder.bind(RequireJsModeHelper.class, RequireJsModeHelperImpl.class); - binder.bind(EsShimDispatcher.class); + binder.bind(EsShimManager.class, EsShimManagerImpl.class); } /** @@ -135,15 +136,15 @@ public static void provideBuiltinJavaScriptStacks(MappedConfiguration - *
requirejs
The RequireJS AMD JavaScript library
+ *
requirejs
The RequireJS AMD JavaScript library (if Require.js is enabled)
*
scriptaculous.js, effects.js
Optional JavaScript libraries in compatibility mode (see {@link Trait#SCRIPTACULOUS})
*
t53-compatibility.js
Optional JavaScript library (see {@link Trait#INITIALIZERS})
- *
underscore-library, underscore-module
+ *
underscore-library, underscore-module (if Require.js is enabled. An ES module version of it is provided by Tapestry)
*
The Underscore JavaScript library, and the shim that allows underscore to be injected
*
t5/core/init
Optional module related to t53-compatibility.js
- *
jquery-library
The jQuery library
- *
jquery-noconflict
Switches jQuery to no-conflict mode (only present when the infrastructure is "prototype").
- *
jquery
A module shim that allows jQuery to be injected (and also switches jQuery to no-conflict mode)
+ *
jquery-library
The jQuery library (if Require.js is enabled. An ES module version of it is provided by Tapestry)
+ *
jquery-noconflict
Switches jQuery to no-conflict mode (only present when the infrastructure is "prototype" and Require.js is enabled).
+ *
jquery
A module shim that allows jQuery to be injected (and also switches jQuery to no-conflict mode) (if Require.js is enabled. An ES module version of it is provided by Tapestry)
*
bootstrap.css, tapestry.css, exception-frame.css, tapestry-console.css, tree.css
*
CSS files
*
t5/core/[...]
@@ -159,10 +160,14 @@ public static void provideBuiltinJavaScriptStacks(MappedConfiguration configuration, Compatibility compatibility, @Symbol(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER) - String provider) + String provider, + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) boolean requireJsEnabled) { - configuration.add("requirejs", StackExtension.library(ROOT + "/require.js")); - configuration.add("underscore-library", StackExtension.library(ROOT + "/underscore-1.13.7.js")); + if (requireJsEnabled) + { + configuration.add("requirejs", StackExtension.library(ROOT + "/require.js")); + configuration.add("underscore-library", StackExtension.library(ROOT + "/underscore-1.13.7.js")); + } if (provider.equals("prototype")) { @@ -180,18 +185,32 @@ public static void setupCoreJavaScriptStack(OrderedConfiguration if (compatibility.enabled(Trait.INITIALIZERS)) { - add(configuration, StackExtensionType.LIBRARY, ROOT + "/t53-compatibility.js"); - configuration.add("t5/core/init", new StackExtension(StackExtensionType.MODULE, "t5/core/init")); + if (requireJsEnabled) + { + add(configuration, StackExtensionType.LIBRARY, ROOT + "/t53-compatibility.js"); + configuration.add("t5/core/init", new StackExtension(StackExtensionType.MODULE, "t5/core/init")); + } + else + { + add(configuration, StackExtensionType.ES_MODULE, "t5/core/t53-compatibility"); + add(configuration, StackExtensionType.ES_MODULE, "t5/core/init"); + } } - configuration.add("jquery-library", StackExtension.library(ROOT + "/jquery.js")); + if (requireJsEnabled) + { + configuration.add("jquery-library", StackExtension.library(ROOT + "/jquery.js")); + } - if (provider.equals("prototype")) + if (provider.equals("prototype") && requireJsEnabled) { configuration.add("jquery-noconflict", StackExtension.library(ROOT + "/jquery-noconflict.js")); } - add(configuration, StackExtensionType.MODULE, "jquery"); + if (requireJsEnabled) + { + add(configuration, StackExtensionType.MODULE, "jquery"); + } add(configuration, StackExtensionType.STYLESHEET, "${" + SymbolConstants.FONT_AWESOME_ROOT + "}/css/font-awesome.css"); @@ -289,11 +308,13 @@ public static void setupModuleDispatchers(OrderedConfiguration confi JavaScriptStackSource javaScriptStackSource, JavaScriptStackPathConstructor javaScriptStackPathConstructor, LocalizationSetter localizationSetter, + EsShimManager esShimManager, + StreamableResourceSource streamableResourceSource, + ResourceChangeTracker resourceChangeTracker, @Symbol(SymbolConstants.MODULE_PATH_PREFIX) String modulePathPrefix, @Symbol(SymbolConstants.ASSET_PATH_PREFIX) - String assetPathPrefix, - EsShimDispatcher esShimDispatcher) + String assetPathPrefix) { configuration.add("Modules", new ModuleDispatcher(moduleManager, resourceStreamer, tracker, pathConstructor, @@ -307,7 +328,13 @@ public static void setupModuleDispatchers(OrderedConfiguration confi assetPathPrefix, true), "after:Modules", "before:ComponentEvent"); - configuration.add("EsShims", esShimDispatcher, "after:Asset", "before:ComponentEvent"); + configuration.add("EsShims", new EsShimDispatcher(esShimManager, streamableResourceSource, resourceChangeTracker, + resourceStreamer, tracker, false), + "before:Asset", "before:ComponentEvent"); + + configuration.add("CompressedEsShims", new EsShimDispatcher(esShimManager, streamableResourceSource, resourceChangeTracker, + resourceStreamer, tracker, true), + "after:EsShims", "before:Asset", "before:ComponentEvent"); } @@ -552,7 +579,7 @@ public static void setupApplicationCatalogEsModules(OrderedConfiguration configuration, @Path("${tapestry.asset.root}/bootstrap/js/transition.js") @@ -560,11 +587,14 @@ public static void setupBaseEsShims( @Path("${tapestry.asset.root}/bootstrap4/js/bootstrap-util.js") Resource bootstrapUtil, Compatibility compatibility, - AssetSource assetSource, - EsShimDispatcher esShimDispatcher) + AssetSource assetSource) { -// configuration.add("jquery", new JavaScriptModuleConfiguration(jqueryShim)); + final Resource jQuery = assetSource.getClasspathAsset("/META-INF/assets/tapestry5/jquery.js") + .getResource(); + configuration.add("jquery", new EsShim(jQuery) + .defaultExport("jQuery.noConflict()") + .getResource()); if (compatibility.enabled(Trait.BOOTSTRAP_3)) { @@ -576,25 +606,23 @@ public static void setupBaseEsShims( final String popoverModuleName = "bootstrap/popover"; configuration.add(popoverModuleName, - EsModuleManagerContribution.base( - new EsShim(popover).importModule("bootstrap/tooltip"), - createCallback(popoverModuleName, esShimDispatcher))); + new EsShim(popover) + .importModule("jquery") + .importModule("bootstrap/tooltip") + .getResource()); } if (compatibility.enabled(Trait.BOOTSTRAP_4)) { final String bootstrapUtilModuleName = "bootstrap/bootstrap-util"; configuration.add(bootstrapUtilModuleName, - EsModuleManagerContribution.base( - new EsShim(bootstrapUtil), - createCallback(bootstrapUtilModuleName, esShimDispatcher))); + new EsShim(bootstrapUtil) + .getResource()); final String popperModuleName = "bootstrap/popper"; final Resource popper = bootstrapUtil.forFile("popper.js"); configuration.add(popperModuleName, - EsModuleManagerContribution.base( - new EsShim(popper), - createCallback(popperModuleName, esShimDispatcher))); + new EsShim(popper).getResource()); for (String name : new String[]{"alert", "button", "carousel", "collapse", "dropdown", "modal", "scrollspy", "tab", "tooltip"}) @@ -604,11 +632,10 @@ public static void setupBaseEsShims( { final String moduleName = "bootstrap/" + name; configuration.add(moduleName, - EsModuleManagerContribution.base( - new EsShim(lib) - .importModule(bootstrapUtilModuleName) - .importModule(popperModuleName), - createCallback(popperModuleName, esShimDispatcher))); + new EsShim(lib) + .importModule(bootstrapUtilModuleName) + .importModule(popperModuleName) + .getResource()); } } } @@ -622,78 +649,16 @@ public static void setupBaseEsShims( } } -// @Contribute(EsModuleManager.class) -// public static void setupBaseEsModules( -// OrderedConfiguration configuration, -// @Path("${tapestry.asset.root}/bootstrap/js/transition.js") -// Resource transition, -// @Path("${tapestry.asset.root}/bootstrap4/js/bootstrap-util.js") -// Resource bootstrapUtil, -// Compatibility compatibility, -// AssetSource assetSource, -// EsShimDispatcher esShimDispatcher) -// { -// -//// configuration.add("jquery", new JavaScriptModuleConfiguration(jqueryShim)); -// -// if (compatibility.enabled(Trait.BOOTSTRAP_3)) -// { -// final String[] modules = new String[]{"affix", "alert", "button", "carousel", "collapse", "dropdown", "modal", -// "scrollspy", "tab", "tooltip"}; -// addBootstrap3EsShims(configuration, modules, transition); -// -// Resource popover = transition.forFile("popover.js"); -// -// final String popoverModuleName = "bootstrap/popover"; -// configuration.add(popoverModuleName, -// EsModuleManagerContribution.base( -// new EsShim(popover).importModule("bootstrap/tooltip"), -// createCallback(popoverModuleName, esShimDispatcher))); -// } -// -// if (compatibility.enabled(Trait.BOOTSTRAP_4)) -// { -// final String bootstrapUtilModuleName = "bootstrap/bootstrap-util"; -// configuration.add(bootstrapUtilModuleName, -// EsModuleManagerContribution.base( -// new EsShim(bootstrapUtil), -// createCallback(bootstrapUtilModuleName, esShimDispatcher))); -// -// final String popperModuleName = "bootstrap/popper"; -// final Resource popper = bootstrapUtil.forFile("popper.js"); -// configuration.add(popperModuleName, -// EsModuleManagerContribution.base( -// new EsShim(popper), -// createCallback(popperModuleName, esShimDispatcher))); -// -// for (String name : new String[]{"alert", "button", "carousel", "collapse", "dropdown", "modal", -// "scrollspy", "tab", "tooltip"}) -// { -// Resource lib = bootstrapUtil.forFile(name + ".js"); -// if (lib.exists()) -// { -// final String moduleName = "bootstrap/" + name; -// configuration.add(moduleName, -// EsModuleManagerContribution.base( -// new EsShim(lib) -// .importModule(bootstrapUtilModuleName) -// .importModule(popperModuleName), -// createCallback(popperModuleName, esShimDispatcher))); -// } -// } -// } -// -// // Just the minimum to have alerts and AJAX validation working when Bootstrap -// // is completely disabled -// if (!compatibility.enabled(Trait.BOOTSTRAP_3) && !compatibility.enabled(Trait.BOOTSTRAP_4)) -// { -// final String[] modules = new String[]{"alert", "dropdown", "collapse"}; -// addBootstrap3EsShims(configuration, modules, transition); -// } -// } - - private static EsModuleConfigurationCallback createCallback(final String moduleName, EsShimDispatcher esShimDispatcher) { - return c -> EsModuleConfigurationCallback.setImport(c, moduleName, esShimDispatcher.getUrl(moduleName)); + @Contribute(EsModuleManager.class) + public static void setupBaseEsModules( + OrderedConfiguration configuration, + EsShimManager esShimManager) + { + for (String moduleName : esShimManager.getShims().keySet()) + { + configuration.add(moduleName, EsModuleManagerContribution.base( + c -> EsModuleConfigurationCallback.setImport(c, moduleName, esShimManager.getUrl(moduleName)))); + } } private static void addBootstrap3EsShims( @@ -707,7 +672,9 @@ private static void addBootstrap3EsShims( final Resource resource = reference.forFile(module + ".js"); if (resource.exists()) { - configuration.add(moduleId, resource); + configuration.add(moduleId, new EsShim(resource) + .importModule("jquery") + .getResource()); } } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java index 73a69419e7..0709fd572a 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java @@ -1810,6 +1810,9 @@ public void contributeMarkupRenderer(OrderedConfiguration @Symbol(SymbolConstants.ENABLE_PAGELOADING_MASK) final boolean enablePageloadingMask, + + @Symbol(SymbolConstants.REQUIRE_JS_ENABLED) + final boolean requireJsEnabled, final ValidationDecoratorFactory validationDecoratorFactory) { @@ -1817,7 +1820,9 @@ public void contributeMarkupRenderer(OrderedConfiguration { public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) { - DocumentLinkerImpl linker = new DocumentLinkerImpl(moduleManager, esModuleManager, omitGeneratorMeta, enablePageloadingMask, tapestryVersion); + DocumentLinkerImpl linker = new DocumentLinkerImpl(moduleManager, + esModuleManager, omitGeneratorMeta, enablePageloadingMask, + tapestryVersion, requireJsEnabled); environment.push(DocumentLinker.class, linker); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java index edd3dfbb01..277d02d993 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java @@ -45,7 +45,7 @@ void writeImportMap(Element head, /** * Invoked by the internal {@link org.apache.tapestry5.internal.services.DocumentLinker} service to write the * ES module imports (as per {@link JavaScriptSupport#importEsModule(String)} into the page. - * this occurs after the ES module infrastructure + * This occurs after the ES module infrastructure * has been written into the page, along with the core libraries. * * @param root @@ -55,6 +55,18 @@ void writeImportMap(Element head, */ void writeImports(Element root, List inits); + + /** + * Invoked by the internal {@link org.apache.tapestry5.internal.services.DocumentLinker} service to write the + * calls to {@code t5/core/pageinit} module. + * this occurs after the ES module infrastructure + * has been written into the page, along with the core libraries. + * + * @param root + * {@code } element of the page. + */ + void writeInitialization(Element body, List libraryURLs); + /** * Encapsulates a contribution to {@linkplain EsModuleManager}. * diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java index ca20f71b45..89a2cde2d1 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShim.java @@ -17,8 +17,9 @@ import java.io.InputStream; import java.io.SequenceInputStream; import java.net.URL; -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.Map; +import java.util.Vector; import org.apache.tapestry5.commons.Resource; import org.apache.tapestry5.internal.util.VirtualResource; @@ -29,7 +30,8 @@ * * @since 5.10.0 */ -public class EsShim { +public class EsShim +{ /** * The underlying resource, usually a JavaScript library @@ -41,9 +43,15 @@ public class EsShim { * the values being the respective parameter names for the module's factory * function. */ - private final Map importConfig = new LinkedHashMap(); + private final Map importConfig = new HashMap(); + + /** + * The default export. + */ + private String defaultExport; - public EsShim(final Resource resource) { + public EsShim(final Resource resource) + { this.resource = resource; } @@ -54,10 +62,11 @@ public EsShim(final Resource resource) { * the name of the required module, e.g. jQuery * @param variableName * the module's corresponding variable name - * @return this ESWrapper for further configuration + * @return this EsShim for further configuration */ public EsShim importModule(final String moduleName, - final String variableName) { + final String variableName) + { importConfig.put(moduleName, variableName); return this; } @@ -70,72 +79,104 @@ public EsShim importModule(final String moduleName, * @param moduleName * the name of the required module, e.g. * bootstrap/transition - * @return this ESWrapper for further configuration + * @return this EsShim for further configuration */ - public EsShim importModule(final String moduleName) { + public EsShim importModule(final String moduleName) + { importConfig.put(moduleName, null); return this; } + + /** + * Defines the default export of this module. + * @param defaultExport the variable or expression to be the + * default export of the module + * @return this EsShim for further configuration + */ + public EsShim defaultExport(final String defaultExport) + { + this.defaultExport = defaultExport; + return this; + } /** - * Returns a virtual resource representing this wrapper. + * Returns a virtual resource representing this shim. * @return a {@linkplain Resource}. */ - public Resource getResource() { - return new ESModuleWrapperResource(resource, importConfig); + public Resource getResource() + { + return new ESModuleWrapperResource(resource, importConfig, defaultExport); } /** - * A virtual resource that wraps a plain JavaScript library as an AMD + * A virtual resource that wraps a plain JavaScript library as an ES * module. - * */ private final static class ESModuleWrapperResource extends VirtualResource { private final Resource resource; private final Map importConfig; + private final String defaultExport; public ESModuleWrapperResource(final Resource resource, - final Map importConfig) + final Map importConfig, + final String defaultExport) { this.resource = resource; this.importConfig = importConfig; + this.defaultExport = defaultExport; } @Override public InputStream openStream() throws IOException { - StringBuilder sb = new StringBuilder(); + StringBuilder imports = new StringBuilder(); for (String module : importConfig.keySet()) { final String variableName = importConfig.get(module); - sb.append("import "); + imports.append("import "); if (variableName != null) { - sb.append(variableName); - sb.append(" from "); + imports.append(variableName); + imports.append(" from "); } - sb.append("\""); - sb.append(module); - sb.append("\";\n"); + imports.append("\""); + imports.append(module); + imports.append("\";\n"); } - return new SequenceInputStream(toInputStream(sb), resource.openStream()); + + // Vector since SequenceInputStream doesn't have a varargs constructor + // nor something better than an Enumeration when you have more than + // 2 InputStreams to join. + Vector v = new Vector<>(); + v.add(toInputStream(imports)); + v.add(resource.openStream()); + if (defaultExport != null) + { + v.add(toInputStream(String.format("\n export default %s;\n", + defaultExport))); + } + + return new SequenceInputStream(v.elements()); } @Override - public String getFile() { + public String getFile() + { return "generated-module-for-" + resource.getFile(); } @Override - public URL toURL() { + public URL toURL() + { return null; } @Override - public String toString() { + public String toString() + { return "ES module wrapper for " + resource.toString(); } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShimManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShimManager.java new file mode 100644 index 0000000000..f9d5c5f139 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsShimManager.java @@ -0,0 +1,49 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.services.javascript; + +import java.util.Map; + +import org.apache.tapestry5.commons.Resource; +import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration; + +/** + * Service managing the ES shims. + * + * @since 5.10.0 + */ +@UsesMappedConfiguration(Resource.class) +public interface EsShimManager +{ + + /** + * Returns the shims as a (module name, module resource) map. + * @return a {@code Map} + */ + Map getShims(); + + /** + * Returns the request prefix to be used for ES shim URLs. + * @param compress a {@code boolean} to inform whether it's the compressed asset URL or not. + * @return the request prefix. + */ + String getRequestPrefix(boolean compress); + + /** + * Returns the full URL of a module. + * @param moduleName a module name. + * @return its URL. + */ + String getUrl(String moduleName); + +} diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/ExtensibleJavaScriptStack.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/ExtensibleJavaScriptStack.java index 8fa030fe5d..0845431ddb 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/ExtensibleJavaScriptStack.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/ExtensibleJavaScriptStack.java @@ -12,6 +12,8 @@ package org.apache.tapestry5.services.javascript; +import java.util.List; + import org.apache.tapestry5.Asset; import org.apache.tapestry5.func.F; import org.apache.tapestry5.func.Flow; @@ -23,8 +25,6 @@ import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.services.AssetSource; -import java.util.List; - /** * An extensible implementation of {@link JavaScriptStack} that can be used as the implementation of a service. * The contributions to the service are used to supply the libraries, stylesheets, and initialization for a @@ -50,6 +50,8 @@ public class ExtensibleJavaScriptStack implements JavaScriptStack private final List stacks; private final List modules; + + private final List esModules; private final String initialization; @@ -116,6 +118,8 @@ public ExtensibleJavaScriptStack(AssetSource assetSource, List c stacks = extensions.filter(by(StackExtensionType.STACK)).map(extractValue).toList(); modules = extensions.filter(by(StackExtensionType.MODULE)).map(extractValue).toList(); + + esModules = extensions.filter(by(StackExtensionType.ES_MODULE)).map(extractValue).toList(); stylesheets = extensions.filter(by(StackExtensionType.STYLESHEET)).map(extractValue).map(stringToAsset) .map(assetToStylesheetLink).toList(); @@ -147,31 +151,41 @@ private JavaScriptAggregationStrategy toStrategy(Flow extensions } } + @Override public List getStacks() { return stacks; } + @Override public List getJavaScriptLibraries() { return libraries; } + @Override public List getStylesheets() { return stylesheets; } + @Override public String getInitialization() { return initialization; } + @Override public List getModules() { return modules; } + @Override + public List getEsModules() { + return esModules; + } + @Override public JavaScriptAggregationStrategy getJavaScriptAggregationStrategy() { diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/JavaScriptStack.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/JavaScriptStack.java index 777c7a118c..25555e3961 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/JavaScriptStack.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/JavaScriptStack.java @@ -72,6 +72,14 @@ public interface JavaScriptStack * @since 5.4 */ List getModules(); + + /** + * Returns a list of ES modules to be automatically included in all pages. + * + * @see EsModuleManager + * @since 5.10.0 + */ + List getEsModules(); /** * Identifies how to aggregate JavaScript within the stack. diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtension.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtension.java index 67cc22d7b4..42c463f38f 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtension.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtension.java @@ -62,6 +62,16 @@ public static StackExtension module(String name) return new StackExtension(StackExtensionType.MODULE, name); } + /** + * Convenience for defining an ES_MODULE. + * + * @since 5.10.0 + */ + public static StackExtension esModule(String name) + { + return new StackExtension(StackExtensionType.ES_MODULE, name); + } + /** * Convenience for defining a STYLESHEET. * diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtensionType.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtensionType.java index a4f0d7b48d..0dccf2e44a 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtensionType.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/StackExtensionType.java @@ -69,6 +69,13 @@ public enum StackExtensionType * @since 5.4 */ MODULE, + + /** + * An ES module to be automatically imported. + * + * @since 5.10.0 + */ + ES_MODULE, /** * Overrides the {@linkplain JavaScriptStack#getJavaScriptAggregationStrategy() JavaScript aggregation strategy} diff --git a/tapestry-core/src/main/resources/META-INF/assets/es-modules/t5/underscore.js b/tapestry-core/src/main/resources/META-INF/assets/es-modules/t5/underscore.js deleted file mode 100644 index dd58992198..0000000000 --- a/tapestry-core/src/main/resources/META-INF/assets/es-modules/t5/underscore.js +++ /dev/null @@ -1,5 +0,0 @@ -// Underscore.js 1.13.7 -// https://underscorejs.org -// (c) 2009-2024 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. -var VERSION="1.13.7",root="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||Function("return this")()||{},ArrayProto=Array.prototype,ObjProto=Object.prototype,SymbolProto="undefined"!=typeof Symbol?Symbol.prototype:null,push=ArrayProto.push,slice=ArrayProto.slice,toString=ObjProto.toString,hasOwnProperty=ObjProto.hasOwnProperty,supportsArrayBuffer="undefined"!=typeof ArrayBuffer,supportsDataView="undefined"!=typeof DataView,nativeIsArray=Array.isArray,nativeKeys=Object.keys,nativeCreate=Object.create,nativeIsView=supportsArrayBuffer&&ArrayBuffer.isView,_isNaN=isNaN,_isFinite=isFinite,hasEnumBug=!{toString:null}.propertyIsEnumerable("toString"),nonEnumerableProps=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],MAX_ARRAY_INDEX=Math.pow(2,53)-1;function restArguments(e,t){return t=null==t?e.length-1:+t,function(){for(var n=Math.max(arguments.length-t,0),r=Array(n),i=0;i=0&&n<=MAX_ARRAY_INDEX}}function shallowProperty(e){return function(t){return null==t?void 0:t[e]}}var getByteLength=shallowProperty("byteLength"),isBufferLike=createSizePropertyCheck(getByteLength),typedArrayPattern=/\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;function isTypedArray(e){return nativeIsView?nativeIsView(e)&&!isDataView$1(e):isBufferLike(e)&&typedArrayPattern.test(toString.call(e))}var isTypedArray$1=supportsArrayBuffer?isTypedArray:constant(!1),getLength=shallowProperty("length");function emulatedSet(e){for(var t={},n=e.length,r=0;r":">",'"':""","'":"'","`":"`"},_escape=createEscaper(escapeMap),unescapeMap=invert(escapeMap),_unescape=createEscaper(unescapeMap),templateSettings=_$1.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},noMatch=/(.)^/,escapes={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},escapeRegExp=/\\|'|\r|\n|\u2028|\u2029/g;function escapeChar(e){return"\\"+escapes[e]}var bareIdentifier=/^\s*(\w|\$)+\s*$/;function template(e,t,n){!t&&n&&(t=n),t=defaults({},t,_$1.templateSettings);var r=RegExp([(t.escape||noMatch).source,(t.interpolate||noMatch).source,(t.evaluate||noMatch).source].join("|")+"|$","g"),i=0,a="__p+='";e.replace(r,(function(t,n,r,u,o){return a+=e.slice(i,o).replace(escapeRegExp,escapeChar),i=o+t.length,n?a+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'":r?a+="'+\n((__t=("+r+"))==null?'':__t)+\n'":u&&(a+="';\n"+u+"\n__p+='"),t})),a+="';\n";var u,o=t.variable;if(o){if(!bareIdentifier.test(o))throw new Error("variable is not a bare identifier: "+o)}else a="with(obj||{}){\n"+a+"}\n",o="obj";a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{u=new Function(o,"_",a)}catch(e){throw e.source=a,e}var s=function(e){return u.call(this,e,_$1)};return s.source="function("+o+"){\n"+a+"}",s}function result(e,t,n){var r=(t=toPath(t)).length;if(!r)return isFunction$1(n)?n.call(e):n;for(var i=0;i1)flatten$1(o,t-1,n,r),i=r.length;else for(var s=0,c=o.length;st?(r&&(clearTimeout(r),r=null),o=c,u=e.apply(i,a),r||(i=a=null)):r||!1===n.trailing||(r=setTimeout(s,f)),u};return c.cancel=function(){clearTimeout(r),o=0,r=i=a=null},c}function debounce(e,t,n){var r,i,a,u,o,s=function(){var c=now()-i;t>c?r=setTimeout(s,t-c):(r=null,n||(u=e.apply(o,a)),r||(a=o=null))},c=restArguments((function(c){return o=this,a=c,i=now(),r||(r=setTimeout(s,t),n&&(u=e.apply(o,a))),u}));return c.cancel=function(){clearTimeout(r),r=a=o=null},c}function wrap(e,t){return partial(t,e)}function negate(e){return function(){return!e.apply(this,arguments)}}function compose(){var e=arguments,t=e.length-1;return function(){for(var n=t,r=e[t].apply(this,arguments);n--;)r=e[n].call(this,r);return r}}function after(e,t){return function(){if(--e<1)return t.apply(this,arguments)}}function before(e,t){var n;return function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=null),n}}var once=partial(before,2);function findKey(e,t,n){t=cb(t,n);for(var r,i=keys(e),a=0,u=i.length;a0?0:i-1;a>=0&&a0?u=a>=0?a:Math.max(a+o,u):o=a>=0?Math.min(a+1,o):a+o+1;else if(n&&a&&o)return r[a=n(r,i)]===i?a:-1;if(i!=i)return(a=t(slice.call(r,u,o),isNaN$1))>=0?a+u:-1;for(a=e>0?u:o-1;a>=0&&a0?0:u-1;for(i||(r=t[a?a[o]:o],o+=e);o>=0&&o=3;return t(e,optimizeCb(n,i,4),r,a)}}var reduce=createReduce(1),reduceRight=createReduce(-1);function filter(e,t,n){var r=[];return t=cb(t,n),each(e,(function(e,n,i){t(e,n,i)&&r.push(e)})),r}function reject(e,t,n){return filter(e,negate(cb(t)),n)}function every(e,t,n){t=cb(t,n);for(var r=!isArrayLike(e)&&keys(e),i=(r||e).length,a=0;a=0}var invoke=restArguments((function(e,t,n){var r,i;return isFunction$1(t)?i=t:(t=toPath(t),r=t.slice(0,-1),t=t[t.length-1]),map(e,(function(e){var a=i;if(!a){if(r&&r.length&&(e=deepGet(e,r)),null==e)return;a=e[t]}return null==a?a:a.apply(e,n)}))}));function pluck(e,t){return map(e,property(t))}function where(e,t){return filter(e,matcher(t))}function max(e,t,n){var r,i,a=-1/0,u=-1/0;if(null==t||"number"==typeof t&&"object"!=typeof e[0]&&null!=e)for(var o=0,s=(e=isArrayLike(e)?e:values(e)).length;oa&&(a=r);else t=cb(t,n),each(e,(function(e,n,r){((i=t(e,n,r))>u||i===-1/0&&a===-1/0)&&(a=e,u=i)}));return a}function min(e,t,n){var r,i,a=1/0,u=1/0;if(null==t||"number"==typeof t&&"object"!=typeof e[0]&&null!=e)for(var o=0,s=(e=isArrayLike(e)?e:values(e)).length;or||void 0===n)return 1;if(n1&&(r=optimizeCb(r,t[1])),t=allKeys(e)):(r=keyInObj,t=flatten$1(t,!1,!1),e=Object(e));for(var i=0,a=t.length;i1&&(n=t[1])):(t=map(flatten$1(t,!1,!1),String),r=function(e,n){return!contains(t,n)}),pick(e,r,n)}));function initial(e,t,n){return slice.call(e,0,Math.max(0,e.length-(null==t||n?1:t)))}function first(e,t,n){return null==e||e.length<1?null==t||n?void 0:[]:null==t||n?e[0]:initial(e,e.length-t)}function rest(e,t,n){return slice.call(e,null==t||n?1:t)}function last(e,t,n){return null==e||e.length<1?null==t||n?void 0:[]:null==t||n?e[e.length-1]:rest(e,Math.max(0,e.length-t))}function compact(e){return filter(e,Boolean)}function flatten(e,t){return flatten$1(e,t,!1)}var difference=restArguments((function(e,t){return t=flatten$1(t,!0,!0),filter(e,(function(e){return!contains(t,e)}))})),without=restArguments((function(e,t){return difference(e,t)}));function uniq(e,t,n,r){isBoolean(t)||(r=n,n=t,t=!1),null!=n&&(n=cb(n,r));for(var i=[],a=[],u=0,o=getLength(e);u ITEMS = CollectionFactory .newList( -// new Item("PublishEventDemo", "@PublishEvent Demo", "Publishing server-side events to client-side code (JavaScript)"), -// -// new Item("StaticActivationContextValueDemo", "@StaticActivationContextValue Demo", "Demonstrates the usage of @StaticActivationContextValue"), -// -// new Item("rest/RestWithOnEventDemo", "REST with @OnEvent Demo", "Demonstrates the usage of @OnEvent to handle REST requests"), -// -// new Item("rest/RestWithEventHandlerMethodNameDemo", "REST with Event Handler Method Name Demo", "Demonstrates the usage of event handler method names to handle REST requests"), -// -// new Item("Html5DateFieldDemo", "Html5DateField Demo", "Choosing dates using the native HTML5 date picker"), -// -//// new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."), -// -// new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."), -// -// new Item("OverrideEventHandlerDemo", "Event Handler Override Demo", "Event Handler methods overridden by sub-classes invoke base-class correctly."), -// -// new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."), -// -// new Item("MissingRequiredARP", "Missing Query Parameter for @ActivationRequestParameter", "Activating a page with a required @ActivationRequestParameter, but no matching query parameter, is an error."), -// -//// new Item("DateFieldValidationDemo", "DateField Validation Demo", -//// "Use of DateField component when client validation is disabled."), -// -// new Item("MixinParameters54", "Strict Mixin Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the mixin id."), -// -// new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."), -// -// new Item("FormCancelActionDemo", "Form Cancel Action Demo", "FormSupport.addCancel() support"), -// -// new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio components inside an Ajax form"), -// -// new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"), -// -// new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"), -// -// new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."), -// -// new Item("MissingAssetDemo", "Missing Asset Demo", "Error when injecting an asset that does not exist."), -// -// new Item("ConfirmDemo", "Confirm Mixin Demo", "Confirm an action when clicking it."), -// -// new Item("SingleErrorDemo", "Single Error", "Using Error component to customize where the errors for a field will be displayed."), -// -// new Item("JavaScriptTests", "JavaScript Tests", "Client-side tests using Mocha and Chai"), -// -// new Item("ModuleInitDemo", "Module-based Initialization Demo", "Invoke a module function to perform page initialization"), -// -// new Item("OperationWorkerDemo", "Operation Worker Demo", "Demonstrate use of @Operation annotation on component methods"), -// -// new Item("MixinParameterDefault", "Mixin Parameter with Default", "Ensure that a mixin parameter with a default value is not reported as unbound."), -// -// new Item("MixinVsInformalParameter", "Mixin Parameter vs. Informal Parameter", "Informal Paramters vs. Mixin parameter of same name"), -// -// new Item("inherit/childa", "TAP5-1656 Demo", "Test a reported bug in component inheritance"), -// -// new Item("ComponentInsideBlockDemo", "Component Inside Block Demo", "Verify that a component, inside a block, is still an embedded "), -// -// new Item("EventMethodUnmatchedComponentId", "Unmatched Component Id in Event Method Demo", "Show that referencing a component that does not exist in an event handler method name is an error."), -// -// new Item("AlertsDemo", "Alerts Demo", "Managing alerts both traditional and Ajax"), -// -// new Item("ClientConsoleDemo", "Client Console Demo", "Demo for the JavaScript client-side console"), -// -// new Item("InvalidFormalParameterDemo", "Unmatched Formal Parameter with @Component", "Parameters specified with @Component annotation must match formal parameters"), -// -// new Item("NullBindingToPrimitive", "Null Bound to Primitive Demo", "Correct exception when a primitive parameter is bound to null"), -// -// new Item("TreeDemo", "Tree Component Demo", "Demo of Tree Component"), -// -// new Item("TreeSelectionDemo", "Tree Component Selection Demo", "Demo of Selection with Tree Component"), -// -// new Item("TreeNoRootsDemo", "Tree Component No Roots Demo", "Demo of No Roots with Tree Component"), -// -// new Item("InvalidExpressionInDynamicTemplate", "Invalid Dynamic Expression", -// "Invalid expression in a Dynamic Template"), -// -// new Item("DynamicDemo", "Dynamic Demo", "Basic Dynamic component tests"), -// -// new Item("DynamicExpansionsDemo", "Expansions in Dynamic Templates", -// "Expansions inside Dynamic component content and attributes"), -// -// new Item("PACAnnotationDemo", "PageActivationContext Demo", -// "Shows that @PageActivationContext fields are set before calls to the activate event handler."), -// -// new Item("PACMultipleAnnotationDemo", "PageActivationContext Multiple Demo", -// "Demonstrates multiple @PageActivationContext fields."), -// -// new Item("PublicFieldAccessDemo", "Public Field Access Demo", "Demonstrates TAP5-1222 fix"), -// -// new Item("ActivationRequestParameterDemo", "ActivationRequestParameter Annotation Demo", -// "Use of @ActivationRequestParameter to encode page state into query parameters"), -// -// new Item("LibraryMessagesDemo", "Library Messages Demo", -// "Demo ability to contribute additional message catalog resources to the application global catalog."), -// -// new Item("MultiZoneUpdateInsideForm", "MultiZone Update inside a Form", -// "Update multiple zones within a single Form."), -// -// new Item("ZoneFormUpdateDemo", "Zone/Form Update Demo", "Updating a Zone inside a Form"), -// -// new Item("MultiZoneStringBodyDemo", "MultiZone String Body Demo", -// "Multi-zone updates in a loop using strings coerced into blocks"), -// -// new Item("RenderNotificationDemo", "RenderNotification Demo", "Use of RenderNotification mixin"), -// -// new Item("InjectMessagesDemo", "Inject Global Messages into Service Demo", -// "Ensure that it is possible to inject the application global message catalog into a service"), -// -// new Item("ReloadDemo", "Reloadable Service Implementation Demo", -// "Used when manually testing service reloads"), -// -// new Item("RequestParameterDemo", "RequestParameter Annotation Demo", -// "Use of @RequestParameter annotation on event handler method parameters"), -// -// new Item("CancelDemo", "Cancel Demo", "Use of the cancel option with Submit"), -// -// new Item("CanceledEventDemo", "Canceled Event Demo", "Triggering of the canceled event from a form."), -// -// new Item("PageResetDemo", "PageReset Annotation Demo", -// "Use of PageReset annotation to re-initialize page state"), -// -// new Item("TestOnlyServiceDemo", "Test Only Service Demo", -// "IoC module available via web.xml configuration"), -// -// new Item("RenderObjectExceptionDemo", "RenderObject Exception Demo", -// "Demonstrate how exceptions when rendering default objects are displayed."), -// -// new Item("MultiLevelInheritDemo", "Multi-Level Inherit Demo", -// "Use of inherit: binding prefix across three levels"), -// -// new Item("HiddenDemo", "Hidden Demo", "Demo the use of the Hidden component."), -// -// new Item("FormZoneDemo", "Form Zone Demo", "Use a form to update a zone."), -// -// new Item("ZoneUpdateNamespace", "Zone/Namespace Interaction", "Prove that TAP5-573 is fixed"), -// -// new Item("AbstractComponentDemo", "Abstract Component Demo", "Error when a component is abstract"), -// -// new Item("TemplateOverrideDemo", "Template Override Demo", -// "Child component extends and overrides parent template."), -// -// new Item("MultiZoneUpdateDemo", "Multiple Zone Update Demo", -// "A single request can now update multiple Zones"), -// -// new Item("LinkSubmitInZoneDemo", "LinkSubmit inside Zone", -// "Ensure that a LinkSubmit works correctly when its containing Form updates a Zone"), -// -// new Item("ProgressiveDemo", "ProgressiveDisplay Demo", "Progressive Enhancement via a component"), -// -// new Item("ClientNumericValidationDemo", "Client-Side Numeric Validation", -// "Client-side locale-specific validation"), -// -// new Item("PublishParametersDemo", "Publish Parameters Demo", -// "Use of @Component.publishParameters attribute."), -// -// new Item("LinkSubmitDemo", "LinkSubmit Demo", "JavaScript LinkSubmit component"), -// -// new Item("LinkSubmitWithoutValidatorDemo", "LinkSubmit Without Validator Demo", -// "Demonstrates that the LinkSubmit component is working without a validator on any of fields in the form"), -// -// new Item("PerFormValidationMessageDemo", "Per-Form Validation Messages", -// "Per-form configuration of validation messages and constraints."), -// -// new Item("EmptyLoopDemo", "Empty Loop Demo", "Use of empty parameter with the Loop component."), -// -// new Item("GenericLoopDemo", "Generic Loop Demo", -// "Use of generic parameters with the Loop component."), -// -// new Item("LoopWithMixinDemo", "Loop With Mixin Demo", -// "Use a mixin with a Loop component."), -// -// new Item("BlankPasswordDemo", "Blank Password Demo", -// "Show that a blank value in a PasswordField does not update the server side value."), -// -// new Item("GridFormEncoderDemo", "Grid Form Encoder Demo", -// "Grid inside a Form using the ValueEncoder option"), -// -// new Item("GridFormWithInitialSortMixinDemo", "Grid Form With Initial Sort Mixin Demo", -// "Grid inside a Form using the InitialSort mixin"), -// -// new Item("DateFieldAjaxFormLoop", "DateField inside AjaxFormLoop", -// "Show that DateField component works correctly inside AjaxFormLoop"), -// -// new Item("NestedForm", "Nested Form Demo", "Error when a Form is nested inside another Form."), -// -// new Item("UnhandledEventDemo", "Unhandled Event Demo", -// "Events that don't have matching event handlers cause exceptions"), -// -// new Item("PrimitiveDefaultDemo", "Primitive Default Demo", -// "Primitive value returned from parameter default method"), -// -// new Item("ValidateFormValidationExceptionDemo", "ValidationForm ValidationException Demo", -// "Throwing a ValidationException from the validateForm event handler."), -// -// new Item("ClientFormatDemo", "Client Format Validation", "Client-side input format validation"), -// -// new Item("ShortGrid", "Short Grid", -// "Grid where the number of claimed rows is less than the number of actual rows"), -// -// new Item("NullParameterDemo", "Null Parameter Demo", "Binding a not-null parameter to null."), -// -// new Item("nestedbeaneditor", "Nested BeanEditor", -// "BeanEditor as override for property editor in BeanEditForm"), -// -// new Item("actionpage", "Action Page", "tests fixture for ActionLink component"), -// -// new Item("cleancachedemo", "Clean Cache Demo", "cache cleared properly during Ajax calls"), -// -// new Item("numberbeaneditordemo", "Number BeanEditor Demo", -// "use of nulls and wrapper types with BeanEditor"), -// -// new Item("forminjectordemo", "FormInjector Demo", "extending a form dynamically via Ajax"), -// -// new Item("music", "Music Page", "demo handling of edge cases of page naming"), -// -// new Item("PersistentDemo", "Persistent Demo", "storing and clearing persistent properties"), -// -// new Item("ActionViaLinkDemo", "Action via Link Demo", "tests creating an action link explicitly"), -// -// new Item("FormFragmentDemo", "Form Fragment Demo", "page with dynamic form sections"), -// -// new Item("BooleanDemo", "Boolean Property Demo", -// "demo boolean properties using both is and get prefixes"), -// -// new Item("DeleteFromGridDemo", "Delete From Grid", "demo deleting items form a Grid"), -// -// new Item("RenderErrorDemo", "Render Error Demo", "reporting of errors while rendering"), -// -// new Item("nested/AssetDemo", "AssetDemo", "declaring an image using Assets"), -// -// new Item("nested/ActionDemo", "Action With Context Demo", -// "using action links with context on page with activation context"), -// -// new Item("blockdemo", "BlockDemo", "use of blocks to control rendering"), -// -// new Item("countdown", "Countdown Page", "defining component using @Component annotation"), -// -// new Item("injectdemo", "Inject Demo", "use of various kinds of injection"), -// -// new Item("instancemixin", "InstanceMixin", "mixin added to a particular component instance"), -// -// new Item("TextFieldWrapperTypeDemo", "TextField Wrapper Types", -// "use of TextField to edit numeric wrapper types (not primitives) "), -// -// new Item("EnvironmentalDemo", "Environmental Annotation Usage", -// "Storing and retrieving Environmental values"), -// -// new Item("Expansion", "Expansion Page", "Use of expansions in templates"), -// -// new Item("ExpansionSubclass", "ExpansionSubclass", -// "components can inherit templates from base classes"), -// -// new Item("Localization", "Localization", "access localized messages from the component catalog"), -// -// new Item("NumberSelect", "NumberSelect", "passivate/activate page context demo"), -// -// new Item("ParameterConflict", "Template Overridden by Class Page", -// "Parameters in the class override those in the template"), -// -// new Item("ParameterDefault", "ParameterDefault", "defaulter methods for component parameters"), -// -// new Item("passwordfielddemo", "PasswordFieldDemo", "test for the PasswordField component"), -// -// new Item("rendercomponentdemo", "RenderComponentDemo", -// "components that \"nominate\" other components to render"), -// -// new Item("renderphaseorder", "RenderPhaseOrder", -// "order of operations when invoking render phase methods"), -// -// new Item("simpleform", "SimpleForm", "first pass at writing Form and TextField components"), -// -// new Item("OptionGroupForm", "OptionGroupForm Demo", "Select with Option Group"), -// -// new Item("validform", "ValidForm", "server-side input validation"), -// -// new Item("ToDoListVolatile", "ToDo List (Volatile)", "Loops and Submit inside Form, volatile mode"), -// -// new Item("MissingTemplate", "Missing Template Demo", -// "Demo for what happens when a template is not found for a page"), -// -// new Item("nested/zonedemo", "Zone Demo", "dynamic updates within a page"), -// -// new Item("todolist", "ToDo List", "Loops and Submit inside Form using primary key encoder"), -// -// new Item("flashdemo", "FlashDemo", "demonstrate 'flash' persistence"), -// -// new Item("beaneditordemo", "BeanEditor Demo", "demonstrate the BeanEditor mega-component"), -// -// new Item("pageloadeddemo", "PageLoaded Demo", "shows that page lifecycle methods are invoked"), -// -// new Item("griddemo", "Grid Demo", "default Grid component"), -// -// new Item("GridInLoopDemo", "Grid In Loop Demo", "Grid inside loop with different model on each iteration"), -// -// new Item("nullgrid", "Null Grid", "handling of null source for Grid"), -// -// new Item("gridsetdemo", "Grid Set Demo", "handling of Set sources for Grid"), -// -// new Item("gridenumdemo", "Grid Enum Demo", "handling of enum types in the Grid"), -// -// new Item("GridRemoveReorderDemo", "Grid Remove/Reorder Demo", -// "handling of remove and reorder parameters"), -// -// new Item("EmptyGrid", "Empty Grid Demo", "show table for empty data sources"), -// -// new Item("GridEarlyPagingDemo", "Grid Early Paging", "set a Grid's current page before rendering"), -// -// new Item("protected", "Protected Page", -// "Demonstrate result of non-void return from a page's activate method"), -// -// new Item("Kicker", "Kicker", "demos complex page and component context in links"), -// -// new Item("simpletrackgriddemo", "SimpleTrack Grid Demo", -// "customizing the model for a Grid around an interface"), -// -// new Item("pagelinkcontext", "PageLink Context Demo", -// "passing explicit context in a page render link"), -// -// new Item("pagecontextinform", "Page Context in Form", "passivate/activate page context in Form", -// "betty", "wilma", "context with spaces", "context/with/slashes"), -// -// new Item("ValidBeanEditorDemo", "Client Validation Demo", "BeanEditor with validation enabled"), -// -// new Item("Unreachable", "Unreachable Page", "page not reachable due to IgnoredPathsFilter"), -// -// new Item("renderabledemo", "Renderable Demo", -// "shows that render phase methods can return a Renderable"), -// -// new Item("inheritedbindingsdemo", "Inherited Bindings Demo", -// "Tests for components that inherit bindings from containing components"), -// -// new Item("ClientPersistenceDemo", "Client Persistence Demo", -// "component field values persisted on the client side"), -// -// new Item("attributeExpansionsDemo", "Attribute Expansions Demo", -// "use expansions inside attributes of ordinary elements"), -// -// new Item("PaletteDemo", "Palette Demo", "multiple selection component"), -// new Item("PaletteGroupedDemo", "Palette Grouped Demo", "multiple selection component (grouped)"), -// -// new Item("ReturnTypes", "Return Types", "tests various event handler return types"), -// -// new Item("FormEncodingType", "Form Encoding Type", -// "Test ability to set an encoding type for a Form"), -// -// new Item("RadioDemo", "RadioDemo", "Use of the RadioGroup and Radio components"), -// -// new Item("RegexpDemo", "Regexp Demo", "Use of the Regexp validator"), -// -// new Item("BeanEditRemoveReorder", "BeanEdit Remove/Reorder", -// "Use of the remove and reorder parameters with BeanEditForm"), -// -// new Item("MultiBeanEditDemo", "MultiBeanEdit Demo", -// "Multiple BeanEditor components in a single form"), -// -// new Item("GridFormDemo", "Grid Form Demo", "Grid operating inside a Form"), -// -// new Item("DateFieldDemo", "DateField Demo", "using DateField by itself on a page"), -// -// new Item("BeanEditDateDemo", "BeanEditor / Date Demo", -// "Use of date properties inside BeanEditor and BeanDisplay"), -// -// new Item("eventmethodtranslate", "EventMethod Translator", -// "Demo ability to provide toclient and parseclient event handler methods"), -// -// new Item("autocompletedemo", "Autocomplete Mixin Demo", -// "Demo the autocomplete mixin for text fields"), -// -// new Item("componentparameter", "ComponentParameter Demo", -// " Demo using a component type as a parameter type and succesfully passing a component"), -// -// new Item("inheritinformalsdemo", "Inherit Informal Parameters Demo", -// "Demo a component which inherits informal parameters from its container"), -// -// new Item("disabledfields", "Disabled Fields", -// "Demonstrate a bunch of disabled fields, to verify that the RenderDisabled mixin works and is being used properly"), -// -// new Item("BeanEditorOverride", "BeanEditor Override", -// "Property editor overrides work for the BeanEditor component itself (not just the BeanEditForm component)"), -// -// new Item("varbindingdemo", "Var Binding Demo", "use of the var: binding prefix"), -// -// new Item("leangriddemo", "Lean Grid Demo", -// "Grid component with lean parameter turned on, to eliminate CSS class attributes in TD and TH elements"), -// -// new Item("blockcaller", "Action Links off of Active Page", -// "Actions can exist on pages other than the active page, via Blocks."), -// -// new Item("unlessdemo", "Unless Demo", "use of the Unless component"), -// -// new Item("delegateinline", "Inline Delegate", -// "Using the delegate component to create inline components"), -// -// new Item("MagicValueEncoder", "Magic ValueEncoder Demo", -// "Automatic creation of ValueEncoder using the TypeCoercer"), -// -// new Item("NullStrategyDemo", "Null Field Strategy Demo", "use of the nulls parameter of TextField"), -// -// new Item("OverrideValidationDecorator", "Override Validation Decorator", -// "override the default validation decorator"), -// -// new Item("ExceptionEventDemo", "Exception Event Demo", "handling component event exceptions"), -// -// new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"), -// -// new Item("PrimitiveArrayParameterDemo", "Primitive Array Parameter Demo", -// "use primitive array as parameter type"), -// -// new Item("RenderPhaseMethodExceptionDemo", "Render Phase Method Exception Demo", -// "render phase methods may throw checked exceptions"), -// -// new Item("TrackEditor", "Generic Page Class Demo", -// "demo use of generics with component classes and, particularily, with property types"), -// -// new Item("IndirectProtectedFields", "Protected Fields Demo", -// "demo exception when component class contains protected fields"), -// -// new Item("injectcomponentdemo", "Inject Component Demo", "inject component defined in template"), -// -// new Item("cachedpage", "Cached Annotation", "Caching method return values"), -// -// new Item("cachedpage2", "Cached Annotation2", "Caching method return values w/ inheritence"), -// -// new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax"), -// -// new Item("methodadvicedemo", "Method Advice Demo", "Advising component methods."), -// -// new Item("HasBodyDemo", "Has Body Demo", "Verify the hasBody() method of ComponentResources"), -// -// new Item("BeanEditorBeanEditContext", "BeanEditor BeanEditContext", -// "BeanEditContext is pushed into enviroment by BeanEditor."), -// -// new Item("InformalParametersDemo", "Informal Parameters Demo", -// "Access to informal parameters names and values"), -// -// new Item("FormFieldOutsideForm", "Form Field Outside Form", -// "Nice exception message for common problem of form fields outside forms"), -// -// new Item("SubmitWithContext", "Submit With Context", "Providing a context for Submit component"), -// -// new Item("MessageConstraintGeneratorDemo", "Validation Constraints From Messages", -// "Providing validators to apply from a properties file"), -// -// new Item("RenderClientIdDemo", "RenderClientId Mixin", -// "Force render of client-side id of a client element via the RenderClientId mixin"), -// -// new Item("BindParameterDemo", "BindParameter mixin annotation", -// "Accessing component parameter values from a mixin"), -// -// new Item("BindParameterNoSuchParameter", "BindParameter error handling", -// "BindParameter throws exception if the containing component doesn't have a matching parameter"), -// -// new Item("BindParameterOnComponent", "BindParameter on component", -// "Verify that BindParameter can only be used on mixin fields"), -// -// new Item("MixinOrderingDemo", "Mixin Ordering Demo", "Various mixin-ordering scenarios"), -// -// new Item( -// "MissingComponentClassException", -// "Missing Component Class Exception", -// "Meaningful exception message thrown when component class can't be determined from template or field in containing component."), -// -// new Item("SessionAttributeDemo", "SessionAttribute Demo", -// "Annotation to map a field to a specific session attribute"), -// -// new Item("BeanEditCalendarDemo", "BeanEditor / Calendar Demo", -// "Use of calendar properties inside BeanEditor and BeanDisplay"), -// -// new Item("TriggerDemo", "Trigger Demo", "Use of Trigger component"), -// -// new Item("ImageSubmitDemo", "Submit with an Image Demo", -// "Make sure that submit with the image parameter set triggers the 'selected' event."), -// -// new Item("SelectZoneDemo", "Select Zone Demo", "Use a Select component to update a zone."), -// -// new Item("AssetProtectionDemo", "Asset Protection Demo", -// "AssetProtectionDispatcher is properly contributed and functioning"), -// -// new Item("BeanDisplayEnumDemo", "BeanDisplay Enum Demo", -// "User represenation of enum values is correctly read from messages"), -// -// new Item("unavailablecomponentdemo", "Report Location of Unavailable Component", -// "Report Location of Unavailable Component"), -// -// new Item("discardafterdemo", "@DiscardAfter Demo", "Demo using @DiscardAfter annotation"), -// -// new Item("SelectDemo", "Select Demo", "Validation decoration for Select"), -// -// new Item("SelectModelFromObjectsAndPropertyNameDemo", "SelectModel from objects and property name", -// "Creating a SelectModel from a list of objects and a label property name"), -// -// new Item("SelectModelFromObjectsDemo", "SelectModel from objects", -// "Creating a SelectModel from a list of objects"), -// -// new Item("SelectModelCoercionDemo", "SelectModel coercion", -// "Creating a SelectModel from a list of objects using coercion"), -// -// new Item("DecoratePageRenderLinkDemo", "Decorate Page Render Link Demo", -// "Decorating page render links"), -// -// new Item("DecorateComponentEventLinkDemo", "Decorate Component Event Link Demo", -// "Decorating event links"), -// -// new Item("ValidatorMacroDemo", "Validator Macro Demo", "Using validator macros"), -// -// new Item("AtInjectDemo", "@jakarta.inject.Inject Demo", "Using @jakarta.inject.Inject for injection"), -// -// new Item("LinkQueryParameters", "Link Query Parameters Demo", -// "Providing Query Parameters directly to link components as a map of key=parameter name, value=parameter values"), -// -// new Item("ChecklistDemo", "Checklist Demo", "Use Checklist component"), -// -// new Item("BeanEditFormPrepareBubbling", "BeanEditor Prepare Bubbling Demo", "Prepare event bubbling"), -// -// new Item("NestedFormFragment", "Nested Form Fragment Demo", "Nesting Form Fragments work properly"), -// -// new Item("MapExpressionInExpansions", "Map Expressions in Expansions Demo", "Maps can be used in expansions"), -// -// new Item("ExpressionInJsFunction", "Expressions in JS Functions Demo", "Expressions can be used inside javascript functions"), -// -// new Item("FormFieldFocusDemo", "FormFieldFocus (DEPRECATED) Demo", "Setting the Form focus on a specific field"), -// -// new Item("FormFragmentExplicitVisibleBoundsDemo", "Form Fragment Explicit Visible Bounds Demo", "Check for form fragment parent visibility can be bounded to"), -// -// new Item("OverrideFieldFocusDemo", "OverrideFieldFocus Demo", "Setting the focus in a form to a specific field"), -// -// new Item("OverrideLabelClassDemo", "Override Label Class Demo", "Setting class attribute on Label component"), -// -// new Item("FormLinkParameters", "FormLinkParameters Demo", "Form link parameters should be unescaped for a hidden field"), -// -// new Item("KnownActivationContextDemo", "Known Activation Context Demo", "Page is displayed normally if called without context (TAP5-2070)", -// "Exact"), -// -// new Item("UnknownActivationContextDemo", "Unknown Activation Context Demo", "Page refuse to serve if called with an unknown activation context (TAP5-2070)", -// "Unwanted", "context"), -// -// new Item("ModuleConfigurationCallbackDemo", "ModuleConfigurationCallback Demo", "Shows an example of changing the Require.js configuration using JavaScriptSupport.addModuleConfigurationDemo()"), -// -// new Item("PartialTemplateRendererDemo", "PartialTemplateRenderer Demo", "Shows some examples of rendering blocks and components to a String using PartialTemplateRenderer"), -// -// new Item("nested/PageThatThrowsException", "Reload on nested page", "Tests a page reload from a nested page's exception report"), -// -// new Item("inplacegridinloopdemo", "In-Place Grid in a Loop Demo", "In-place grid in a loop"), -// -// new Item("GenericTypeDemo", "Generic bound type demo", "Tests that generic type info is available for generic bindings"), -// -// new Item("FormFieldClientIdParameterDemo", "Form Field clientId Parameter Demo", "Shows and tests how to explicitly set the id of a form field component"), -// -// new Item("gridwithsubmitwithcontextdemo", "Grid with Submit with context", "A grid whose rows contain a Submit component with context"), -// -// new Item("textfieldwithnullvalidateparameter", "TextField with null validate parameter", "A TextField whose validate parameter is bound to null"), -// -// new Item("validateCheckboxMustBeChecked", "Validate Checkbox Must Be Checked", "A form that trigger validate in " + -// "error event on submit when checkbox is not checked"), -// -// new Item("validateCheckboxMustBeUnchecked", "Validate Checkbox Must Be Unchecked", "A form that trigger validate in " + -// "error event on submit when checkbox is checked"), -// -// new Item("validateInErrorEvent", "Validate in error Event", "A form that trigger validate in " + -// "error event on submit when textfield is empty"), -// -// new Item("onactivateredirect", "OnActivateRedirect Demo", "A page that redirects to itself from" -// + " its activation method"), -// -// new Item("BeanEditorWithFormFragmentDemo", "Bean Editor With Form Fragment Demo", "TriggerFragment mixin used inside a BeanEditor"), -// -// new Item("ObjectEditorDemo","Object Editor Demo","Edit Bean with address objects"), -// -// new Item("IfDemo","If Demo","If component with all its options"), -// -// new Item("RecursiveDemo","Recursive Demo","Recursive component example"), -// -// new Item("SelfRecursiveDemo", "Self-Recursive Demo", "check for handling of self-recursive components"), -// -// new Item("EsModuleDemo", "ES Module Demo", "tests and demonstrations for the ES module support") + new Item("PublishEventDemo", "@PublishEvent Demo", "Publishing server-side events to client-side code (JavaScript)"), + + new Item("StaticActivationContextValueDemo", "@StaticActivationContextValue Demo", "Demonstrates the usage of @StaticActivationContextValue"), + + new Item("rest/RestWithOnEventDemo", "REST with @OnEvent Demo", "Demonstrates the usage of @OnEvent to handle REST requests"), + + new Item("rest/RestWithEventHandlerMethodNameDemo", "REST with Event Handler Method Name Demo", "Demonstrates the usage of event handler method names to handle REST requests"), + + new Item("Html5DateFieldDemo", "Html5DateField Demo", "Choosing dates using the native HTML5 date picker"), + +// new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."), + + new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."), + + new Item("OverrideEventHandlerDemo", "Event Handler Override Demo", "Event Handler methods overridden by sub-classes invoke base-class correctly."), + + new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."), + + new Item("MissingRequiredARP", "Missing Query Parameter for @ActivationRequestParameter", "Activating a page with a required @ActivationRequestParameter, but no matching query parameter, is an error."), + +// new Item("DateFieldValidationDemo", "DateField Validation Demo", +// "Use of DateField component when client validation is disabled."), + + new Item("MixinParameters54", "Strict Mixin Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the mixin id."), + + new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."), + + new Item("FormCancelActionDemo", "Form Cancel Action Demo", "FormSupport.addCancel() support"), + + new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio components inside an Ajax form"), + + new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"), + + new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"), + + new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."), + + new Item("MissingAssetDemo", "Missing Asset Demo", "Error when injecting an asset that does not exist."), + + new Item("ConfirmDemo", "Confirm Mixin Demo", "Confirm an action when clicking it."), + + new Item("SingleErrorDemo", "Single Error", "Using Error component to customize where the errors for a field will be displayed."), + + new Item("JavaScriptTests", "JavaScript Tests", "Client-side tests using Mocha and Chai"), + + new Item("ModuleInitDemo", "Module-based Initialization Demo", "Invoke a module function to perform page initialization"), + + new Item("OperationWorkerDemo", "Operation Worker Demo", "Demonstrate use of @Operation annotation on component methods"), + + new Item("MixinParameterDefault", "Mixin Parameter with Default", "Ensure that a mixin parameter with a default value is not reported as unbound."), + + new Item("MixinVsInformalParameter", "Mixin Parameter vs. Informal Parameter", "Informal Paramters vs. Mixin parameter of same name"), + + new Item("inherit/childa", "TAP5-1656 Demo", "Test a reported bug in component inheritance"), + + new Item("ComponentInsideBlockDemo", "Component Inside Block Demo", "Verify that a component, inside a block, is still an embedded "), + + new Item("EventMethodUnmatchedComponentId", "Unmatched Component Id in Event Method Demo", "Show that referencing a component that does not exist in an event handler method name is an error."), + + new Item("AlertsDemo", "Alerts Demo", "Managing alerts both traditional and Ajax"), + + new Item("ClientConsoleDemo", "Client Console Demo", "Demo for the JavaScript client-side console"), + + new Item("InvalidFormalParameterDemo", "Unmatched Formal Parameter with @Component", "Parameters specified with @Component annotation must match formal parameters"), + + new Item("NullBindingToPrimitive", "Null Bound to Primitive Demo", "Correct exception when a primitive parameter is bound to null"), + + new Item("TreeDemo", "Tree Component Demo", "Demo of Tree Component"), + + new Item("TreeSelectionDemo", "Tree Component Selection Demo", "Demo of Selection with Tree Component"), + + new Item("TreeNoRootsDemo", "Tree Component No Roots Demo", "Demo of No Roots with Tree Component"), + + new Item("InvalidExpressionInDynamicTemplate", "Invalid Dynamic Expression", + "Invalid expression in a Dynamic Template"), + + new Item("DynamicDemo", "Dynamic Demo", "Basic Dynamic component tests"), + + new Item("DynamicExpansionsDemo", "Expansions in Dynamic Templates", + "Expansions inside Dynamic component content and attributes"), + + new Item("PACAnnotationDemo", "PageActivationContext Demo", + "Shows that @PageActivationContext fields are set before calls to the activate event handler."), + + new Item("PACMultipleAnnotationDemo", "PageActivationContext Multiple Demo", + "Demonstrates multiple @PageActivationContext fields."), + + new Item("PublicFieldAccessDemo", "Public Field Access Demo", "Demonstrates TAP5-1222 fix"), + + new Item("ActivationRequestParameterDemo", "ActivationRequestParameter Annotation Demo", + "Use of @ActivationRequestParameter to encode page state into query parameters"), + + new Item("LibraryMessagesDemo", "Library Messages Demo", + "Demo ability to contribute additional message catalog resources to the application global catalog."), + + new Item("MultiZoneUpdateInsideForm", "MultiZone Update inside a Form", + "Update multiple zones within a single Form."), + + new Item("ZoneFormUpdateDemo", "Zone/Form Update Demo", "Updating a Zone inside a Form"), + + new Item("MultiZoneStringBodyDemo", "MultiZone String Body Demo", + "Multi-zone updates in a loop using strings coerced into blocks"), + + new Item("RenderNotificationDemo", "RenderNotification Demo", "Use of RenderNotification mixin"), + + new Item("InjectMessagesDemo", "Inject Global Messages into Service Demo", + "Ensure that it is possible to inject the application global message catalog into a service"), + + new Item("ReloadDemo", "Reloadable Service Implementation Demo", + "Used when manually testing service reloads"), + + new Item("RequestParameterDemo", "RequestParameter Annotation Demo", + "Use of @RequestParameter annotation on event handler method parameters"), + + new Item("CancelDemo", "Cancel Demo", "Use of the cancel option with Submit"), + + new Item("CanceledEventDemo", "Canceled Event Demo", "Triggering of the canceled event from a form."), + + new Item("PageResetDemo", "PageReset Annotation Demo", + "Use of PageReset annotation to re-initialize page state"), + + new Item("TestOnlyServiceDemo", "Test Only Service Demo", + "IoC module available via web.xml configuration"), + + new Item("RenderObjectExceptionDemo", "RenderObject Exception Demo", + "Demonstrate how exceptions when rendering default objects are displayed."), + + new Item("MultiLevelInheritDemo", "Multi-Level Inherit Demo", + "Use of inherit: binding prefix across three levels"), + + new Item("HiddenDemo", "Hidden Demo", "Demo the use of the Hidden component."), + + new Item("FormZoneDemo", "Form Zone Demo", "Use a form to update a zone."), + + new Item("ZoneUpdateNamespace", "Zone/Namespace Interaction", "Prove that TAP5-573 is fixed"), + + new Item("AbstractComponentDemo", "Abstract Component Demo", "Error when a component is abstract"), + + new Item("TemplateOverrideDemo", "Template Override Demo", + "Child component extends and overrides parent template."), + + new Item("MultiZoneUpdateDemo", "Multiple Zone Update Demo", + "A single request can now update multiple Zones"), + + new Item("LinkSubmitInZoneDemo", "LinkSubmit inside Zone", + "Ensure that a LinkSubmit works correctly when its containing Form updates a Zone"), + + new Item("ProgressiveDemo", "ProgressiveDisplay Demo", "Progressive Enhancement via a component"), + + new Item("ClientNumericValidationDemo", "Client-Side Numeric Validation", + "Client-side locale-specific validation"), + + new Item("PublishParametersDemo", "Publish Parameters Demo", + "Use of @Component.publishParameters attribute."), + + new Item("LinkSubmitDemo", "LinkSubmit Demo", "JavaScript LinkSubmit component"), + + new Item("LinkSubmitWithoutValidatorDemo", "LinkSubmit Without Validator Demo", + "Demonstrates that the LinkSubmit component is working without a validator on any of fields in the form"), + + new Item("PerFormValidationMessageDemo", "Per-Form Validation Messages", + "Per-form configuration of validation messages and constraints."), + + new Item("EmptyLoopDemo", "Empty Loop Demo", "Use of empty parameter with the Loop component."), + + new Item("GenericLoopDemo", "Generic Loop Demo", + "Use of generic parameters with the Loop component."), + + new Item("LoopWithMixinDemo", "Loop With Mixin Demo", + "Use a mixin with a Loop component."), + + new Item("BlankPasswordDemo", "Blank Password Demo", + "Show that a blank value in a PasswordField does not update the server side value."), + + new Item("GridFormEncoderDemo", "Grid Form Encoder Demo", + "Grid inside a Form using the ValueEncoder option"), + + new Item("GridFormWithInitialSortMixinDemo", "Grid Form With Initial Sort Mixin Demo", + "Grid inside a Form using the InitialSort mixin"), + + new Item("DateFieldAjaxFormLoop", "DateField inside AjaxFormLoop", + "Show that DateField component works correctly inside AjaxFormLoop"), + + new Item("NestedForm", "Nested Form Demo", "Error when a Form is nested inside another Form."), + + new Item("UnhandledEventDemo", "Unhandled Event Demo", + "Events that don't have matching event handlers cause exceptions"), + + new Item("PrimitiveDefaultDemo", "Primitive Default Demo", + "Primitive value returned from parameter default method"), + + new Item("ValidateFormValidationExceptionDemo", "ValidationForm ValidationException Demo", + "Throwing a ValidationException from the validateForm event handler."), + + new Item("ClientFormatDemo", "Client Format Validation", "Client-side input format validation"), + + new Item("ShortGrid", "Short Grid", + "Grid where the number of claimed rows is less than the number of actual rows"), + + new Item("NullParameterDemo", "Null Parameter Demo", "Binding a not-null parameter to null."), + + new Item("nestedbeaneditor", "Nested BeanEditor", + "BeanEditor as override for property editor in BeanEditForm"), + + new Item("actionpage", "Action Page", "tests fixture for ActionLink component"), + + new Item("cleancachedemo", "Clean Cache Demo", "cache cleared properly during Ajax calls"), + + new Item("numberbeaneditordemo", "Number BeanEditor Demo", + "use of nulls and wrapper types with BeanEditor"), + + new Item("forminjectordemo", "FormInjector Demo", "extending a form dynamically via Ajax"), + + new Item("music", "Music Page", "demo handling of edge cases of page naming"), + + new Item("PersistentDemo", "Persistent Demo", "storing and clearing persistent properties"), + + new Item("ActionViaLinkDemo", "Action via Link Demo", "tests creating an action link explicitly"), + + new Item("FormFragmentDemo", "Form Fragment Demo", "page with dynamic form sections"), + + new Item("BooleanDemo", "Boolean Property Demo", + "demo boolean properties using both is and get prefixes"), + + new Item("DeleteFromGridDemo", "Delete From Grid", "demo deleting items form a Grid"), + + new Item("RenderErrorDemo", "Render Error Demo", "reporting of errors while rendering"), + + new Item("nested/AssetDemo", "AssetDemo", "declaring an image using Assets"), + + new Item("nested/ActionDemo", "Action With Context Demo", + "using action links with context on page with activation context"), + + new Item("blockdemo", "BlockDemo", "use of blocks to control rendering"), + + new Item("countdown", "Countdown Page", "defining component using @Component annotation"), + + new Item("injectdemo", "Inject Demo", "use of various kinds of injection"), + + new Item("instancemixin", "InstanceMixin", "mixin added to a particular component instance"), + + new Item("TextFieldWrapperTypeDemo", "TextField Wrapper Types", + "use of TextField to edit numeric wrapper types (not primitives) "), + + new Item("EnvironmentalDemo", "Environmental Annotation Usage", + "Storing and retrieving Environmental values"), + + new Item("Expansion", "Expansion Page", "Use of expansions in templates"), + + new Item("ExpansionSubclass", "ExpansionSubclass", + "components can inherit templates from base classes"), + + new Item("Localization", "Localization", "access localized messages from the component catalog"), + + new Item("NumberSelect", "NumberSelect", "passivate/activate page context demo"), + + new Item("ParameterConflict", "Template Overridden by Class Page", + "Parameters in the class override those in the template"), + + new Item("ParameterDefault", "ParameterDefault", "defaulter methods for component parameters"), + + new Item("passwordfielddemo", "PasswordFieldDemo", "test for the PasswordField component"), + + new Item("rendercomponentdemo", "RenderComponentDemo", + "components that \"nominate\" other components to render"), + + new Item("renderphaseorder", "RenderPhaseOrder", + "order of operations when invoking render phase methods"), + + new Item("simpleform", "SimpleForm", "first pass at writing Form and TextField components"), + + new Item("OptionGroupForm", "OptionGroupForm Demo", "Select with Option Group"), + + new Item("validform", "ValidForm", "server-side input validation"), + + new Item("ToDoListVolatile", "ToDo List (Volatile)", "Loops and Submit inside Form, volatile mode"), + + new Item("MissingTemplate", "Missing Template Demo", + "Demo for what happens when a template is not found for a page"), + + new Item("nested/zonedemo", "Zone Demo", "dynamic updates within a page"), + + new Item("todolist", "ToDo List", "Loops and Submit inside Form using primary key encoder"), + + new Item("flashdemo", "FlashDemo", "demonstrate 'flash' persistence"), + + new Item("beaneditordemo", "BeanEditor Demo", "demonstrate the BeanEditor mega-component"), + + new Item("pageloadeddemo", "PageLoaded Demo", "shows that page lifecycle methods are invoked"), + + new Item("griddemo", "Grid Demo", "default Grid component"), + + new Item("GridInLoopDemo", "Grid In Loop Demo", "Grid inside loop with different model on each iteration"), + + new Item("nullgrid", "Null Grid", "handling of null source for Grid"), + + new Item("gridsetdemo", "Grid Set Demo", "handling of Set sources for Grid"), + + new Item("gridenumdemo", "Grid Enum Demo", "handling of enum types in the Grid"), + + new Item("GridRemoveReorderDemo", "Grid Remove/Reorder Demo", + "handling of remove and reorder parameters"), + + new Item("EmptyGrid", "Empty Grid Demo", "show table for empty data sources"), + + new Item("GridEarlyPagingDemo", "Grid Early Paging", "set a Grid's current page before rendering"), + + new Item("protected", "Protected Page", + "Demonstrate result of non-void return from a page's activate method"), + + new Item("Kicker", "Kicker", "demos complex page and component context in links"), + + new Item("simpletrackgriddemo", "SimpleTrack Grid Demo", + "customizing the model for a Grid around an interface"), + + new Item("pagelinkcontext", "PageLink Context Demo", + "passing explicit context in a page render link"), + + new Item("pagecontextinform", "Page Context in Form", "passivate/activate page context in Form", + "betty", "wilma", "context with spaces", "context/with/slashes"), + + new Item("ValidBeanEditorDemo", "Client Validation Demo", "BeanEditor with validation enabled"), + + new Item("Unreachable", "Unreachable Page", "page not reachable due to IgnoredPathsFilter"), + + new Item("renderabledemo", "Renderable Demo", + "shows that render phase methods can return a Renderable"), + + new Item("inheritedbindingsdemo", "Inherited Bindings Demo", + "Tests for components that inherit bindings from containing components"), + + new Item("ClientPersistenceDemo", "Client Persistence Demo", + "component field values persisted on the client side"), + + new Item("attributeExpansionsDemo", "Attribute Expansions Demo", + "use expansions inside attributes of ordinary elements"), + + new Item("PaletteDemo", "Palette Demo", "multiple selection component"), + new Item("PaletteGroupedDemo", "Palette Grouped Demo", "multiple selection component (grouped)"), + + new Item("ReturnTypes", "Return Types", "tests various event handler return types"), + + new Item("FormEncodingType", "Form Encoding Type", + "Test ability to set an encoding type for a Form"), + + new Item("RadioDemo", "RadioDemo", "Use of the RadioGroup and Radio components"), + + new Item("RegexpDemo", "Regexp Demo", "Use of the Regexp validator"), + + new Item("BeanEditRemoveReorder", "BeanEdit Remove/Reorder", + "Use of the remove and reorder parameters with BeanEditForm"), + + new Item("MultiBeanEditDemo", "MultiBeanEdit Demo", + "Multiple BeanEditor components in a single form"), + + new Item("GridFormDemo", "Grid Form Demo", "Grid operating inside a Form"), + + new Item("DateFieldDemo", "DateField Demo", "using DateField by itself on a page"), + + new Item("BeanEditDateDemo", "BeanEditor / Date Demo", + "Use of date properties inside BeanEditor and BeanDisplay"), + + new Item("eventmethodtranslate", "EventMethod Translator", + "Demo ability to provide toclient and parseclient event handler methods"), + + new Item("autocompletedemo", "Autocomplete Mixin Demo", + "Demo the autocomplete mixin for text fields"), + + new Item("componentparameter", "ComponentParameter Demo", + " Demo using a component type as a parameter type and succesfully passing a component"), + + new Item("inheritinformalsdemo", "Inherit Informal Parameters Demo", + "Demo a component which inherits informal parameters from its container"), + + new Item("disabledfields", "Disabled Fields", + "Demonstrate a bunch of disabled fields, to verify that the RenderDisabled mixin works and is being used properly"), + + new Item("BeanEditorOverride", "BeanEditor Override", + "Property editor overrides work for the BeanEditor component itself (not just the BeanEditForm component)"), + + new Item("varbindingdemo", "Var Binding Demo", "use of the var: binding prefix"), + + new Item("leangriddemo", "Lean Grid Demo", + "Grid component with lean parameter turned on, to eliminate CSS class attributes in TD and TH elements"), + + new Item("blockcaller", "Action Links off of Active Page", + "Actions can exist on pages other than the active page, via Blocks."), + + new Item("unlessdemo", "Unless Demo", "use of the Unless component"), + + new Item("delegateinline", "Inline Delegate", + "Using the delegate component to create inline components"), + + new Item("MagicValueEncoder", "Magic ValueEncoder Demo", + "Automatic creation of ValueEncoder using the TypeCoercer"), + + new Item("NullStrategyDemo", "Null Field Strategy Demo", "use of the nulls parameter of TextField"), + + new Item("OverrideValidationDecorator", "Override Validation Decorator", + "override the default validation decorator"), + + new Item("ExceptionEventDemo", "Exception Event Demo", "handling component event exceptions"), + + new Item("AddedGridColumnsDemo", "Added Grid Columns Demo", "programatically adding grid columns"), + + new Item("PrimitiveArrayParameterDemo", "Primitive Array Parameter Demo", + "use primitive array as parameter type"), + + new Item("RenderPhaseMethodExceptionDemo", "Render Phase Method Exception Demo", + "render phase methods may throw checked exceptions"), + + new Item("TrackEditor", "Generic Page Class Demo", + "demo use of generics with component classes and, particularily, with property types"), + + new Item("IndirectProtectedFields", "Protected Fields Demo", + "demo exception when component class contains protected fields"), + + new Item("injectcomponentdemo", "Inject Component Demo", "inject component defined in template"), + + new Item("cachedpage", "Cached Annotation", "Caching method return values"), + + new Item("cachedpage2", "Cached Annotation2", "Caching method return values w/ inheritence"), + + new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax"), + + new Item("methodadvicedemo", "Method Advice Demo", "Advising component methods."), + + new Item("HasBodyDemo", "Has Body Demo", "Verify the hasBody() method of ComponentResources"), + + new Item("BeanEditorBeanEditContext", "BeanEditor BeanEditContext", + "BeanEditContext is pushed into enviroment by BeanEditor."), + + new Item("InformalParametersDemo", "Informal Parameters Demo", + "Access to informal parameters names and values"), + + new Item("FormFieldOutsideForm", "Form Field Outside Form", + "Nice exception message for common problem of form fields outside forms"), + + new Item("SubmitWithContext", "Submit With Context", "Providing a context for Submit component"), + + new Item("MessageConstraintGeneratorDemo", "Validation Constraints From Messages", + "Providing validators to apply from a properties file"), + + new Item("RenderClientIdDemo", "RenderClientId Mixin", + "Force render of client-side id of a client element via the RenderClientId mixin"), + + new Item("BindParameterDemo", "BindParameter mixin annotation", + "Accessing component parameter values from a mixin"), + + new Item("BindParameterNoSuchParameter", "BindParameter error handling", + "BindParameter throws exception if the containing component doesn't have a matching parameter"), + + new Item("BindParameterOnComponent", "BindParameter on component", + "Verify that BindParameter can only be used on mixin fields"), + + new Item("MixinOrderingDemo", "Mixin Ordering Demo", "Various mixin-ordering scenarios"), + + new Item( + "MissingComponentClassException", + "Missing Component Class Exception", + "Meaningful exception message thrown when component class can't be determined from template or field in containing component."), + + new Item("SessionAttributeDemo", "SessionAttribute Demo", + "Annotation to map a field to a specific session attribute"), + + new Item("BeanEditCalendarDemo", "BeanEditor / Calendar Demo", + "Use of calendar properties inside BeanEditor and BeanDisplay"), + + new Item("TriggerDemo", "Trigger Demo", "Use of Trigger component"), + + new Item("ImageSubmitDemo", "Submit with an Image Demo", + "Make sure that submit with the image parameter set triggers the 'selected' event."), + + new Item("SelectZoneDemo", "Select Zone Demo", "Use a Select component to update a zone."), + + new Item("AssetProtectionDemo", "Asset Protection Demo", + "AssetProtectionDispatcher is properly contributed and functioning"), + + new Item("BeanDisplayEnumDemo", "BeanDisplay Enum Demo", + "User represenation of enum values is correctly read from messages"), + + new Item("unavailablecomponentdemo", "Report Location of Unavailable Component", + "Report Location of Unavailable Component"), + + new Item("discardafterdemo", "@DiscardAfter Demo", "Demo using @DiscardAfter annotation"), + + new Item("SelectDemo", "Select Demo", "Validation decoration for Select"), + + new Item("SelectModelFromObjectsAndPropertyNameDemo", "SelectModel from objects and property name", + "Creating a SelectModel from a list of objects and a label property name"), + + new Item("SelectModelFromObjectsDemo", "SelectModel from objects", + "Creating a SelectModel from a list of objects"), + + new Item("SelectModelCoercionDemo", "SelectModel coercion", + "Creating a SelectModel from a list of objects using coercion"), + + new Item("DecoratePageRenderLinkDemo", "Decorate Page Render Link Demo", + "Decorating page render links"), + + new Item("DecorateComponentEventLinkDemo", "Decorate Component Event Link Demo", + "Decorating event links"), + + new Item("ValidatorMacroDemo", "Validator Macro Demo", "Using validator macros"), + + new Item("AtInjectDemo", "@jakarta.inject.Inject Demo", "Using @jakarta.inject.Inject for injection"), + + new Item("LinkQueryParameters", "Link Query Parameters Demo", + "Providing Query Parameters directly to link components as a map of key=parameter name, value=parameter values"), + + new Item("ChecklistDemo", "Checklist Demo", "Use Checklist component"), + + new Item("BeanEditFormPrepareBubbling", "BeanEditor Prepare Bubbling Demo", "Prepare event bubbling"), + + new Item("NestedFormFragment", "Nested Form Fragment Demo", "Nesting Form Fragments work properly"), + + new Item("MapExpressionInExpansions", "Map Expressions in Expansions Demo", "Maps can be used in expansions"), + + new Item("ExpressionInJsFunction", "Expressions in JS Functions Demo", "Expressions can be used inside javascript functions"), + + new Item("FormFieldFocusDemo", "FormFieldFocus (DEPRECATED) Demo", "Setting the Form focus on a specific field"), + + new Item("FormFragmentExplicitVisibleBoundsDemo", "Form Fragment Explicit Visible Bounds Demo", "Check for form fragment parent visibility can be bounded to"), + + new Item("OverrideFieldFocusDemo", "OverrideFieldFocus Demo", "Setting the focus in a form to a specific field"), + + new Item("OverrideLabelClassDemo", "Override Label Class Demo", "Setting class attribute on Label component"), + + new Item("FormLinkParameters", "FormLinkParameters Demo", "Form link parameters should be unescaped for a hidden field"), + + new Item("KnownActivationContextDemo", "Known Activation Context Demo", "Page is displayed normally if called without context (TAP5-2070)", + "Exact"), + + new Item("UnknownActivationContextDemo", "Unknown Activation Context Demo", "Page refuse to serve if called with an unknown activation context (TAP5-2070)", + "Unwanted", "context"), + + new Item("ModuleConfigurationCallbackDemo", "ModuleConfigurationCallback Demo", "Shows an example of changing the Require.js configuration using JavaScriptSupport.addModuleConfigurationDemo()"), + + new Item("PartialTemplateRendererDemo", "PartialTemplateRenderer Demo", "Shows some examples of rendering blocks and components to a String using PartialTemplateRenderer"), + + new Item("nested/PageThatThrowsException", "Reload on nested page", "Tests a page reload from a nested page's exception report"), + + new Item("inplacegridinloopdemo", "In-Place Grid in a Loop Demo", "In-place grid in a loop"), + + new Item("GenericTypeDemo", "Generic bound type demo", "Tests that generic type info is available for generic bindings"), + + new Item("FormFieldClientIdParameterDemo", "Form Field clientId Parameter Demo", "Shows and tests how to explicitly set the id of a form field component"), + + new Item("gridwithsubmitwithcontextdemo", "Grid with Submit with context", "A grid whose rows contain a Submit component with context"), + + new Item("textfieldwithnullvalidateparameter", "TextField with null validate parameter", "A TextField whose validate parameter is bound to null"), + + new Item("validateCheckboxMustBeChecked", "Validate Checkbox Must Be Checked", "A form that trigger validate in " + + "error event on submit when checkbox is not checked"), + + new Item("validateCheckboxMustBeUnchecked", "Validate Checkbox Must Be Unchecked", "A form that trigger validate in " + + "error event on submit when checkbox is checked"), + + new Item("validateInErrorEvent", "Validate in error Event", "A form that trigger validate in " + + "error event on submit when textfield is empty"), + + new Item("onactivateredirect", "OnActivateRedirect Demo", "A page that redirects to itself from" + + " its activation method"), + + new Item("BeanEditorWithFormFragmentDemo", "Bean Editor With Form Fragment Demo", "TriggerFragment mixin used inside a BeanEditor"), + + new Item("ObjectEditorDemo","Object Editor Demo","Edit Bean with address objects"), + + new Item("IfDemo","If Demo","If component with all its options"), + + new Item("RecursiveDemo","Recursive Demo","Recursive component example"), + + new Item("SelfRecursiveDemo", "Self-Recursive Demo", "check for handling of self-recursive components"), + + new Item("EsModuleDemo", "ES Module Demo", "tests and demonstrations for the ES module support") ); static diff --git a/tapestry-core/src/test/resources/META-INF/assets/es-modules/app/test-support.js b/tapestry-core/src/test/resources/META-INF/assets/es-modules/app/test-support.js new file mode 100644 index 0000000000..7728ef0c18 --- /dev/null +++ b/tapestry-core/src/test/resources/META-INF/assets/es-modules/app/test-support.js @@ -0,0 +1,14 @@ +// Provide test support functions that can be addressed via Selenium. + +// TODO: Maybe move this to main, for external re-use? + +import dom from "t5/core/dom"; + +const exports = { + findCSSMatchCount(selector) { return dom.body.find(selector).length; }, + doesNotExist(elementId) { return (dom(elementId)) === null; } +}; + +window.testSupport = exports; + +export default exports; From 95b0b5a1cb9c2ea54d439744397b58fd08578d44 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Sat, 26 Jul 2025 19:10:56 -0300 Subject: [PATCH 05/30] TAP5-2810: fixing ES modules and pageinit relationship --- .../services/EsModuleInitsManager.java | 39 ++++++++++++++++++- .../services/PartialMarkupDocumentLinker.java | 12 +++++- .../ajax/EsModuleInitializationImpl.java | 10 +++-- .../javascript/EsModuleManagerImpl.java | 20 +++++----- .../main/typescript/src/t5/core/pageinit.ts | 13 +++++-- .../integration/app1/pages/EsModuleDemo.java | 2 +- .../integration/app1/services/AppModule.java | 2 +- .../javascript/EsModuleManagerImplTest.java | 39 +++++++++++-------- .../resources/META-INF/assets/zonedemo.js | 22 ++++++++++- 9 files changed, 117 insertions(+), 42 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java index e3fc767fe4..04f3a2a672 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java @@ -25,11 +25,14 @@ package org.apache.tapestry5.internal.services; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.tapestry5.commons.util.CollectionFactory; import org.apache.tapestry5.internal.services.ajax.EsModuleInitializationImpl; +import org.apache.tapestry5.json.JSONArray; import org.apache.tapestry5.services.javascript.EsModuleInitialization; public class EsModuleInitsManager @@ -37,13 +40,13 @@ public class EsModuleInitsManager private final Set modules = CollectionFactory.newSet(); private final List initializations = CollectionFactory.newList(); - + public void add(EsModuleInitialization initialization) { assert initialization != null; // We ignore a module being added again. - final String moduleName = ((EsModuleInitializationImpl) initialization).getModuleId(); + final String moduleName = ((EsModuleInitializationImpl) initialization).getModuleName(); if (!modules.contains(moduleName)) { initializations.add(initialization); @@ -58,4 +61,36 @@ public List getInits() { return initializations; } + + /** + * Returns all previously added inits as JSONArray instances. + */ + public List getInitsAsJsonArrays() + { + + List list; + if (!initializations.isEmpty()) + { + list = new ArrayList<>(initializations.size()); + for (EsModuleInitialization init : initializations) + { + final EsModuleInitializationImpl initImpl = (EsModuleInitializationImpl) init; + final JSONArray arguments = initImpl.getArguments(); + if (arguments != null) + { + list.add(new JSONArray(initImpl.getModuleName(), arguments)); + } + else + { + list.add(new JSONArray().put(initImpl.getModuleName())); + } + } + } + else + { + list = Collections.emptyList(); + } + return list; + } + } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java index 9ccf816295..ae06144596 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java @@ -24,6 +24,7 @@ import org.apache.tapestry5.services.javascript.ModuleConfigurationCallback; import org.apache.tapestry5.services.javascript.StylesheetLink; +import java.util.ArrayList; import java.util.List; public class PartialMarkupDocumentLinker implements DocumentLinker @@ -34,6 +35,8 @@ public class PartialMarkupDocumentLinker implements DocumentLinker private final ModuleInitsManager initsManager = new ModuleInitsManager(); + private final EsModuleInitsManager esModulesinitsManager = new EsModuleInitsManager(); + public void addCoreLibrary(String libraryURL) { notImplemented("addCoreLibrary"); @@ -84,7 +87,7 @@ public void addEsModuleConfigurationCallback(EsModuleConfigurationCallback callb @Override public void addEsModuleInitialization(EsModuleInitialization initialization) { - notImplemented("addEsModuleInitialization"); + esModulesinitsManager.add(initialization); } /** @@ -106,10 +109,15 @@ public void commit(JSONObject reply) } List inits = initsManager.getSortedInits(); + final List esModuleInits = esModulesinitsManager.getInitsAsJsonArrays(); - if (inits.size() > 0) + if (inits.size() > 0 || esModuleInits.size() > 0) { + List allInits = new ArrayList<>(inits.size() + esModuleInits.size()); + allInits.addAll(inits); + allInits.addAll(esModuleInits); reply.in(InternalConstants.PARTIAL_KEY).put("inits", JSONArray.from(inits)); } + } } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java index eb4e321370..1fd947592b 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ajax/EsModuleInitializationImpl.java @@ -14,6 +14,7 @@ import java.util.Map; import org.apache.tapestry5.commons.util.CollectionFactory; +import org.apache.tapestry5.json.JSONArray; import org.apache.tapestry5.services.javascript.EsModuleInitialization; import org.apache.tapestry5.services.javascript.ImportPlacement; @@ -22,7 +23,7 @@ public class EsModuleInitializationImpl extends BaseInitialization attributes; private ImportPlacement placement = ImportPlacement.BODY_BOTTOM; - private Object[] arguments; + private JSONArray arguments; public EsModuleInitializationImpl(String moduleName) { @@ -45,7 +46,7 @@ public EsModuleInitialization placement(ImportPlacement placement) return null; } - public String getModuleId() { + public String getModuleName() { return moduleName; } @@ -66,10 +67,11 @@ public String getFunctionName() { @Override public void with(Object... arguments) { - this.arguments = arguments; + assert arguments != null; + this.arguments = new JSONArray(arguments); } - public Object[] getArguments() + public JSONArray getArguments() { return arguments; } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java index 5cfebf058f..6c56cbf525 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java @@ -218,7 +218,7 @@ public void writeImports(Element root, List inits) ImportPlacement placement; EsModuleInitializationImpl init; String functionName; - Object[] arguments; + JSONArray arguments; List allInits = new ArrayList<>(coreStackInits.size() + inits.size()); allInits.addAll(coreStackInits); @@ -228,7 +228,7 @@ public void writeImports(Element root, List inits) { init = (EsModuleInitializationImpl) i; - final String moduleId = init.getModuleId(); + final String moduleId = init.getModuleName(); // Making sure the user doesn't shoot their own foot final String url = cache.get(moduleId); if (url == null) @@ -329,26 +329,26 @@ private String convert(List input) } - static String convertToJsFunctionParameters(Object[] arguments, boolean compactJSON) + static String convertToJsFunctionParameters(JSONArray arguments, boolean compactJSON) { String result; - if (arguments == null || arguments.length == 0) + if (arguments == null || arguments.size() == 0) { result = ""; } - else if (arguments.length == 1) + else if (arguments.size() == 1) { - result = convertToJsFunctionParameter(arguments[0], compactJSON); + result = convertToJsFunctionParameter(arguments.get(0), compactJSON); } else { StringBuilder builder = new StringBuilder(); - for (int i = 0; i < arguments.length; i++) + for (int i = 0; i < arguments.size(); i++) { if (i > 0) { builder.append(", "); } - builder.append(convertToJsFunctionParameter(arguments[i], compactJSON)); + builder.append(convertToJsFunctionParameter(arguments.get(i), compactJSON)); } result = builder.toString(); } @@ -359,9 +359,9 @@ static String convertToJsFunctionParameter(Object object, boolean compactJSON) { String result; - if (object == null) + if (object == null || object == JSONObject.NULL) { - result = null; + result = "null"; } else if (object instanceof String || object instanceof JSONLiteral) { diff --git a/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts b/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts index 5100e68bd9..197af36806 100644 --- a/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts +++ b/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts @@ -35,6 +35,8 @@ const isOpera = Object.prototype.toString.call(window.opera) === '[object Opera] // @ts-ignore const isIE = !!window.attachEvent && !isOpera; +const requireJsEnabled = "true" == document.querySelector("body")?.dataset['requireJsEnabled']; + const rebuildURL = function(path: string) { if (path.match(/^https?:/)) { return path; } @@ -160,8 +162,13 @@ function loadLibraries(libraries: string[], callback: () => any) { // @ts-ignore const reducer = (callback, library) => (function() { console.debug(`Loading library ${library}`); - // @ts-ignore - return require([library], callback); + if (requireJsEnabled) { + // @ts-ignore + return require([library], callback); + } + else { + return import(library).then(callback); + } }); const finalCallback = _.reduceRight(libraries, reducer, callback); @@ -250,7 +257,7 @@ export default exports_ = _.extend(loadLibrariesAndInitialize, { // second, which helps ensure that other initializions on the page are in place. // // * fieldId - element id of field to focus on - focus(fieldId: string) { + focus: function(fieldId: string) { const field = dom(fieldId); if (field) { diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EsModuleDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EsModuleDemo.java index de17500ba2..f58e2c674b 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EsModuleDemo.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/EsModuleDemo.java @@ -69,7 +69,7 @@ void importEsModule() .with(); javaScriptSupport.importEsModule("parameter-type-default-export") - .with(null, true, false, Math.PI * Math.E, "string", "jsonLiteral", + .with(JSONObject.NULL, true, false, Math.PI * Math.E, "string", "jsonLiteral", new JSONObject("key", "value"), new JSONArray(1, "2")); if (overrideEsModuleImportAgain != null && overrideEsModuleImportAgain) diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java index d067f8cac6..b5c7017cc0 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java @@ -187,7 +187,7 @@ public static void contributeApplicationDefaults(MappedConfiguration dom.default.onDocument(events.default.zone.didUpdate, () => (dom.default("zone-update-message")).update("Zone updated."))); + +function execute(dom, events) { + dom.default.onDocument(events.default.zone.didUpdate, function() { + dom.default("zone-update-message").update("Zone updated."); + }); +} + +if (typeof require !== 'undefined') { + require(["t5/core/dom", "t5/core/events"], function(dom, events) { + execute(dom, events); + }); + +} +else { + import("t5/core/dom").then((dom) => { + import("t5/core/events").then((events) => { + execute(dom, events); + }); + }); +} \ No newline at end of file From f2894a40352e2d3899fcd9ada29f59e01860eb11 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Sun, 27 Jul 2025 11:51:39 -0300 Subject: [PATCH 06/30] TAP5-2804: Fixing dynamic locale message imports --- .../src/main/typescript/src/t5/core/messages-es-module.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tapestry-core/src/main/typescript/src/t5/core/messages-es-module.ts b/tapestry-core/src/main/typescript/src/t5/core/messages-es-module.ts index afd3d4d8a2..8b77d441f4 100644 --- a/tapestry-core/src/main/typescript/src/t5/core/messages-es-module.ts +++ b/tapestry-core/src/main/typescript/src/t5/core/messages-es-module.ts @@ -43,6 +43,10 @@ const locale = (document.documentElement.getAttribute("data-locale")) || "en"; // @ts-ignore let messages: any = await import(`t5/core/messages/${locale}`); +if (messages['default'] != null && typeof messages['default'] === 'object') { + messages = messages.default; +} + // Returns the application message catalog message for the given key. Returns // a placeholder if the key is not found. const get = function(key: string) { From 74120c30f88637b92515e282fd0e48a9c9754f00 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Sun, 27 Jul 2025 20:23:56 -0300 Subject: [PATCH 07/30] TAP5-2810: fixing ES modules imports with function calls --- .../javascript/EsModuleManagerImpl.java | 22 ++++--- .../tapestry5/modules/JavaScriptModule.java | 58 +++++++++++-------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java index 6c56cbf525..b0f4090d28 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/EsModuleManagerImpl.java @@ -281,22 +281,26 @@ else if (placement.equals(ImportPlacement.BODY_TOP)) // If we have not only the import, but also an automatic function call if (arguments != null || functionName != null) { + + // TODO: move this logic to a pageinit call, like AMD does + // t5/core/pageinit:loadLibrariesAndInitialize final Element moduleFunctionCall = script.element("script"); moduleFunctionCall.moveAfter(script); final String moduleFunctionCallFormat = - "import %s from '%s';\n" - + "%s(%s);"; - - final String importName = functionName != null ? functionName : GENERIC_IMPORTED_VARIABLE; - final String importDeclaration = functionName != null ? - "{ " + functionName + " }": - GENERIC_IMPORTED_VARIABLE; + "import m from '%s';\n" + + "import console from 't5/core/console';\n" + + "\nif (console.debugEnabled) {" + + "\n console.debug('Invoking %1$s:%2$s(' + (Array.from(%4$s).map(function(arg) { return JSON.stringify(arg); })).join(\", \") + ')');" + + "\n m.%2$s(%3$s);" + + "\n}\n"; moduleFunctionCall.text(String.format(moduleFunctionCallFormat, - importDeclaration, moduleId, importName, - convertToJsFunctionParameters(arguments, compactJSON))); + moduleId, + functionName, + convertToJsFunctionParameters(arguments, compactJSON), + arguments)); writeAttributes(moduleFunctionCall, init); diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java index 3ea83348c7..cbf0120a99 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java @@ -556,28 +556,28 @@ public static void setupApplicationCatalogModules(MappedConfiguration configuration, - LocalizationSetter localizationSetter, - ComponentMessagesSource messagesSource, - ResourceChangeTracker resourceChangeTracker, - @Symbol(SymbolConstants.COMPACT_JSON) boolean compactJSON) - { - - EsModuleConfigurationCallback callback = jsonObject -> { - - for (Locale locale : localizationSetter.getSupportedLocales()) - { - MessageCatalogResource resource = new MessageCatalogResource(false, locale, messagesSource, resourceChangeTracker, compactJSON); - - jsonObject.put("t5/core/messages/" + locale.toString(), resource.toURL()); - } - - }; - - configuration.add("ApplicationCatalog", EsModuleManagerContribution.base(callback)); - - } +// @Contribute(EsModuleManager.class) +// public static void setupApplicationCatalogEsModules(OrderedConfiguration configuration, +// LocalizationSetter localizationSetter, +// ComponentMessagesSource messagesSource, +// ResourceChangeTracker resourceChangeTracker, +// @Symbol(SymbolConstants.COMPACT_JSON) boolean compactJSON) +// { +// +// EsModuleConfigurationCallback callback = jsonObject -> { +// +// for (Locale locale : localizationSetter.getSupportedLocales()) +// { +// MessageCatalogResource resource = new MessageCatalogResource(false, locale, messagesSource, resourceChangeTracker, compactJSON); +// +// jsonObject.put("t5/core/messages/" + locale.toString(), resource.toURL()); +// } +// +// }; +// +// configuration.add("ApplicationCatalog", EsModuleManagerContribution.base(callback)); +// +// } @Contribute(EsShimManager.class) public static void setupBaseEsShims( @@ -587,7 +587,11 @@ public static void setupBaseEsShims( @Path("${tapestry.asset.root}/bootstrap4/js/bootstrap-util.js") Resource bootstrapUtil, Compatibility compatibility, - AssetSource assetSource) + AssetSource assetSource, + LocalizationSetter localizationSetter, + ComponentMessagesSource messagesSource, + ResourceChangeTracker resourceChangeTracker, + @Symbol(SymbolConstants.COMPACT_JSON) boolean compactJSON) { final Resource jQuery = assetSource.getClasspathAsset("/META-INF/assets/tapestry5/jquery.js") @@ -647,6 +651,14 @@ public static void setupBaseEsShims( final String[] modules = new String[]{"alert", "dropdown", "collapse"}; addBootstrap3EsShims(configuration, modules, transition); } + + for (Locale locale : localizationSetter.getSupportedLocales()) + { + MessageCatalogResource resource = new MessageCatalogResource(true, locale, messagesSource, resourceChangeTracker, compactJSON); + configuration.add("t5/core/messages/" + locale.toString(), + new EsShim(resource).getResource()); + } + } @Contribute(EsModuleManager.class) From 4f1e2f75984027b6607f3ab33bfc314c6926f3d6 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 07:23:11 -0300 Subject: [PATCH 08/30] TAP5-2810: fixing ES modules imports with function calls in AJAX request --- .../services/EsModuleInitsManager.java | 13 ++++--- .../services/PartialMarkupDocumentLinker.java | 2 +- .../main/typescript/src/t5/core/pageinit.ts | 35 ++++++++++++++----- .../es-modules/app/multi-zone-update.js | 5 +++ 4 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 tapestry-core/src/test/resources/META-INF/assets/es-modules/app/multi-zone-update.js diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java index 04f3a2a672..96a01f1d0d 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java @@ -76,14 +76,17 @@ public List getInitsAsJsonArrays() { final EsModuleInitializationImpl initImpl = (EsModuleInitializationImpl) init; final JSONArray arguments = initImpl.getArguments(); + final JSONArray initArray = new JSONArray(); + + initArray.add(initImpl.getModuleName()); + if (arguments != null) { - list.add(new JSONArray(initImpl.getModuleName(), arguments)); - } - else - { - list.add(new JSONArray().put(initImpl.getModuleName())); + initArray.addAll(arguments); } + + list.add(initArray); + } } else diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java index ae06144596..7cd6a770ad 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PartialMarkupDocumentLinker.java @@ -116,7 +116,7 @@ public void commit(JSONObject reply) List allInits = new ArrayList<>(inits.size() + esModuleInits.size()); allInits.addAll(inits); allInits.addAll(esModuleInits); - reply.in(InternalConstants.PARTIAL_KEY).put("inits", JSONArray.from(inits)); + reply.in(InternalConstants.PARTIAL_KEY).put("inits", JSONArray.from(allInits)); } } diff --git a/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts b/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts index 197af36806..85d13124ca 100644 --- a/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts +++ b/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts @@ -111,15 +111,8 @@ const addStylesheets = function(newStylesheets: StylesheetLink) { const invokeInitializer = function(tracker: () => any, qualifiedName: string, initArguments: any[]) { const [moduleName, functionName] = Array.from(qualifiedName.split(':')); - // @ts-ignore - return require([moduleName], function(moduleLib: any) { + function executeInitializer(moduleLib: any) { - // If it's an AMD module generated by TypeScript and it has a default export, - // it gets wrapped, so we try to unwrap it here. - if (moduleLib != null && moduleLib.__esModule && moduleLib["default"] != null) { - moduleLib = moduleLib["default"]; - } - try { // Some modules export nothing but do some full-page initialization, such as adding // event handlers to the body. @@ -153,7 +146,31 @@ const invokeInitializer = function(tracker: () => any, qualifiedName: string, in } finally { tracker(); } - }); + + } + + if (requireJsEnabled) { + + // @ts-ignore + return require([moduleName], function(moduleLib: any) { + + // If it's an AMD module generated by TypeScript and it has a default export, + // it gets wrapped, so we try to unwrap it here. + if (moduleLib != null && moduleLib.__esModule && moduleLib["default"] != null) { + moduleLib = moduleLib["default"]; + } + + return executeInitializer(moduleLib); + + }); + + } else { + + return import(moduleName).then(function(moduleLib: any) { + return executeInitializer(moduleLib['default']); + }); + + } }; // Pre-loads a number of libraries in order. When the last library is loaded, diff --git a/tapestry-core/src/test/resources/META-INF/assets/es-modules/app/multi-zone-update.js b/tapestry-core/src/test/resources/META-INF/assets/es-modules/app/multi-zone-update.js new file mode 100644 index 0000000000..411363128f --- /dev/null +++ b/tapestry-core/src/test/resources/META-INF/assets/es-modules/app/multi-zone-update.js @@ -0,0 +1,5 @@ +import dom from "t5/core/dom"; + +export default function (id, message) { + dom(id).update(message); +}; \ No newline at end of file From dd308204581d4c025a1f8d7aa839791c60844ca9 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 08:05:37 -0300 Subject: [PATCH 09/30] TAP5-2810: enabling tests with Require.js disabled --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index a80b696370..4a4e5f07f4 100755 --- a/build.gradle +++ b/build.gradle @@ -539,9 +539,9 @@ task combinedJacocoReport(type:JacocoReport){ task continuousIntegration { // tapestry-javadoc doesn't work with Java 8 anymore. That's why it's only added if != 8. - def dependants = [subprojects.build, // jQuery and Require.js enabled -// 'tapestry-core:testWithJqueryAndRequireJsDisabled', -// 'tapestry-core:testWithPrototypeAndRequireJsEnabled', + def dependants = [subprojects.build, + 'tapestry-core:testWithJqueryAndRequireJsDisabled', + 'tapestry-core:testWithPrototypeAndRequireJsEnabled', 'tapestry-core:testWithPrototypeAndRequireJsDisabled', combinedJacocoReport] if (JavaVersion.current() != JavaVersion.VERSION_1_8) { From 7fed99bcaaea9a35bf4b63de7042784e82ec1c6f Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 08:12:25 -0300 Subject: [PATCH 10/30] TAP5-2810: JavaDoc fix. --- .../tapestry5/services/javascript/EsModuleManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java index 277d02d993..cc57b47fed 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/javascript/EsModuleManager.java @@ -62,8 +62,9 @@ void writeImportMap(Element head, * this occurs after the ES module infrastructure * has been written into the page, along with the core libraries. * - * @param root - * {@code } element of the page. + * @param body {@code body} element of the page. + * @param libraryURLs URLs of the JS files to be included in the page. + * */ void writeInitialization(Element body, List libraryURLs); From ff412df3df2ae0e2270c7a7b24f9f90cbb60aa5a Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 20:05:26 -0300 Subject: [PATCH 11/30] TAP5-2810: fixing import of BeanValidator JS module --- .../beanvalidator/modules/BeanValidatorModule.java | 5 ++--- tapestry-core/build.gradle | 9 +++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java index d41a3d25e4..e068bb5146 100644 --- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java +++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/modules/BeanValidatorModule.java @@ -44,7 +44,6 @@ public class BeanValidatorModule { - private static final String VALIDATION_MODULE_NAME = "t5/core/validation"; private static final String MODULE_NAME = "t5/beanvalidator/beanvalidator-validation"; public static void bind(final ServiceBinder binder) @@ -100,8 +99,8 @@ public static void contributeClientConstraintDescriptorSource(final JavaScriptSu { final Runnable importJs = requireJsEnabled ? - () -> javaScriptSupport.require(VALIDATION_MODULE_NAME) : - () -> javaScriptSupport.importEsModule(VALIDATION_MODULE_NAME); + () -> javaScriptSupport.require(MODULE_NAME) : + () -> javaScriptSupport.importEsModule(MODULE_NAME); configuration.add(new BaseCCD(Max.class, "value") { diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle index 1e66536cd1..6d18543f54 100644 --- a/tapestry-core/build.gradle +++ b/tapestry-core/build.gradle @@ -147,16 +147,25 @@ task runTestAppfolder(type:JavaExec) { // other combinations task testWithJqueryAndRequireJsDisabled(type:Test) { + print("-------------------------------------); + print("Test suite with jQuery as infrastructure and Require.js disabled") + print("-------------------------------------); systemProperties."tapestry.javascript-infrastructure-provider" = "jquery" systemProperties."tapestry.require-js-enabled" = "false" } task testWithPrototypeAndRequireJsEnabled(type:Test) { + print("-------------------------------------); + print("Test suite with Prototype.js as infrastructure and Require.js enabled") + print("-------------------------------------); systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" systemProperties."tapestry.require-js-enabled" = "true" } task testWithPrototypeAndRequireJsDisabled(type:Test) { + print("-------------------------------------); + print("Test suite with Prototype.js as infrastructure and Require.js disabled") + print("-------------------------------------); systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" systemProperties."tapestry.require-js-enabled" = "false" } \ No newline at end of file From 10e2bb128c1d40bffa2ce695af3f6d25b172b99e Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 20:07:23 -0300 Subject: [PATCH 12/30] TAP5-2810: fixing syntax error in build.gradle --- tapestry-core/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle index 6d18543f54..f41e8b466e 100644 --- a/tapestry-core/build.gradle +++ b/tapestry-core/build.gradle @@ -147,7 +147,7 @@ task runTestAppfolder(type:JavaExec) { // other combinations task testWithJqueryAndRequireJsDisabled(type:Test) { - print("-------------------------------------); + print("-------------------------------------"); print("Test suite with jQuery as infrastructure and Require.js disabled") print("-------------------------------------); systemProperties."tapestry.javascript-infrastructure-provider" = "jquery" @@ -155,7 +155,7 @@ task testWithJqueryAndRequireJsDisabled(type:Test) { } task testWithPrototypeAndRequireJsEnabled(type:Test) { - print("-------------------------------------); + print("-------------------------------------"); print("Test suite with Prototype.js as infrastructure and Require.js enabled") print("-------------------------------------); systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" @@ -163,7 +163,7 @@ task testWithPrototypeAndRequireJsEnabled(type:Test) { } task testWithPrototypeAndRequireJsDisabled(type:Test) { - print("-------------------------------------); + print("-------------------------------------"); print("Test suite with Prototype.js as infrastructure and Require.js disabled") print("-------------------------------------); systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" From c468d8061850e5d974eb28d2b0c6703448d38dc9 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 20:11:22 -0300 Subject: [PATCH 13/30] TAP5-2810: fixing another syntax error in build.gradle --- tapestry-core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle index f41e8b466e..7f36317715 100644 --- a/tapestry-core/build.gradle +++ b/tapestry-core/build.gradle @@ -165,7 +165,7 @@ task testWithPrototypeAndRequireJsEnabled(type:Test) { task testWithPrototypeAndRequireJsDisabled(type:Test) { print("-------------------------------------"); print("Test suite with Prototype.js as infrastructure and Require.js disabled") - print("-------------------------------------); + print("-------------------------------------"); systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" systemProperties."tapestry.require-js-enabled" = "false" } \ No newline at end of file From 7425c430e5acd30a4a5f66d8048528077007c8bc Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 20:18:15 -0300 Subject: [PATCH 14/30] TAP5-2810: fixing another syntax error in build.gradle again --- tapestry-core/build.gradle | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle index 7f36317715..cbab38d33a 100644 --- a/tapestry-core/build.gradle +++ b/tapestry-core/build.gradle @@ -147,25 +147,25 @@ task runTestAppfolder(type:JavaExec) { // other combinations task testWithJqueryAndRequireJsDisabled(type:Test) { - print("-------------------------------------"); - print("Test suite with jQuery as infrastructure and Require.js disabled") - print("-------------------------------------); + println "-------------------------------------" + println "Test suite with jQuery as infrastructure and Require.js disabled" + println "-------------------------------------" systemProperties."tapestry.javascript-infrastructure-provider" = "jquery" systemProperties."tapestry.require-js-enabled" = "false" } task testWithPrototypeAndRequireJsEnabled(type:Test) { - print("-------------------------------------"); - print("Test suite with Prototype.js as infrastructure and Require.js enabled") - print("-------------------------------------); + println "-------------------------------------" + println "Test suite with Prototype.js as infrastructure and Require.js enabled" + println "-------------------------------------" systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" systemProperties."tapestry.require-js-enabled" = "true" } task testWithPrototypeAndRequireJsDisabled(type:Test) { - print("-------------------------------------"); - print("Test suite with Prototype.js as infrastructure and Require.js disabled") - print("-------------------------------------"); + println "-------------------------------------" + println "Test suite with Prototype.js as infrastructure and Require.js disabled" + println "-------------------------------------" systemProperties."tapestry.javascript-infrastructure-provider" = "prototype" systemProperties."tapestry.require-js-enabled" = "false" } \ No newline at end of file From 78e85db65553b2a1e2e3d9019b869d6d1cd26f65 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 20:45:09 -0300 Subject: [PATCH 15/30] TAP5-2810: DocumentLinkerImpl tests --- .../services/DocumentLinkerImplTest.groovy | 63 ++++++++++++++----- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/DocumentLinkerImplTest.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/DocumentLinkerImplTest.groovy index a9eafe7a77..8a86f58ad6 100644 --- a/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/DocumentLinkerImplTest.groovy +++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/services/DocumentLinkerImplTest.groovy @@ -5,6 +5,7 @@ import org.apache.tapestry5.dom.Element import org.apache.tapestry5.dom.XMLMarkupModel import org.apache.tapestry5.internal.test.InternalBaseTestCase import org.apache.tapestry5.json.JSONArray +import org.apache.tapestry5.services.javascript.EsModuleManager import org.apache.tapestry5.services.javascript.InitializationPriority import org.apache.tapestry5.services.javascript.ModuleManager import org.apache.tapestry5.services.javascript.StylesheetLink @@ -32,7 +33,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("not-html").text("not an HTML document") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) // Only checked if there's something to link. @@ -55,7 +56,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("not-html").text("not an HTML document") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) // Only checked if there's something to link. @@ -76,7 +77,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { void missing_root_element_is_a_noop() { Document document = new Document() - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) linker.addLibrary("foo.js") linker.addScript(InitializationPriority.NORMAL, "doSomething();") @@ -93,8 +94,9 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html").element("body").element("p").text("Ready to be updated with scripts.") def manager = mockModuleManager(["core.js", "foo.js", "bar/baz.js"], [new JSONArray("t5/core/pageinit:evalJavaScript", "pageINIT();")]) + def esManager = mockEsModuleManager([], []) - DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, esManager, true, false, "1.2.3", true) replay() @@ -122,7 +124,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html").element("body").element("p").text("Ready to be marked with generator meta.") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, false, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, false, false, "1.2.3", true) linker.updateDocument(document) @@ -141,7 +143,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("no_html").text("Generator meta only added if root is html tag.") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, false, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, false, false, "1.2.3", true) linker.updateDocument(document) @@ -158,7 +160,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html").element("body").element("p").text("Ready to be updated with styles.") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) linker.addStylesheetLink(new StylesheetLink("foo.css")) linker.addStylesheetLink(new StylesheetLink("bar/baz.css", new StylesheetOptions("print"))) @@ -178,7 +180,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html").element("head").comment(" existing head ").container.element("body").text( "body content") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) linker.addStylesheetLink(new StylesheetLink("foo.css")) @@ -197,8 +199,9 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html").element("body").element("p").text("Ready to be updated with scripts.") def manager = mockModuleManager([], [new JSONArray("t5/core/pageinit:evalJavaScript", "doSomething();")]) + def esManager = mockEsModuleManager([], []) - DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, null, true, true, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, esManager, true, true, "1.2.3", true) replay() @@ -223,8 +226,9 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html").element("notbody").element("p").text("Ready to be updated with scripts.") def manager = mockModuleManager(["foo.js"], []) + def esManager = mockEsModuleManager([], []) - DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, esManager, true, false, "1.2.3", true) replay() @@ -250,8 +254,9 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { head.element("script") def manager = mockModuleManager([], [new JSONArray("['immediate/module:myfunc', {'fred':'barney'}]")]) + def esManager = mockEsModuleManager([], []) - DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, esManager, true, false, "1.2.3", true) replay() @@ -273,7 +278,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) linker.addStylesheetLink(new StylesheetLink("everybody.css")) linker.addStylesheetLink(new StylesheetLink("just_ie.css", new StylesheetOptions().withCondition("IE"))) @@ -295,7 +300,7 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { document.newRootElement("html") - DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(null, null, true, false, "1.2.3", true) linker.addStylesheetLink(new StylesheetLink("whatever.css")) linker.addStylesheetLink(new StylesheetLink("insertion-point.css", new StylesheetOptions().asAjaxInsertionPoint())) @@ -318,8 +323,10 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { def manager = mockModuleManager([], ["my/module", new JSONArray("my/other/module:normal", 111, 222), new JSONArray("my/other/module:late", 333, 444)]) + + def esManager = mockEsModuleManager([], []) - DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, esManager, true, false, "1.2.3", true) replay() @@ -346,8 +353,10 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { def manager = mockModuleManager([], ["my/module", new JSONArray("my/other/module:normal", 111, 222)]) + + def esManager = mockEsModuleManager([], []) - DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, null, true, false, "1.2.3") + DocumentLinkerImpl linker = new DocumentLinkerImpl(manager, esManager, true, false, "1.2.3", true) replay() @@ -386,4 +395,28 @@ class DocumentLinkerImplTest extends InternalBaseTestCase { return mock; } + + private EsModuleManager mockEsModuleManager(def libraryURLs, def inits) { + + EsModuleManager mock = newMock(EsModuleManager); + +/* expect(mock.writeConfiguration(isA(Element), + eq([]))).andAnswer({ + def body = EasyMock.currentArguments[0] + + body.comment("MM-CONFIG") + } as IAnswer).once() + + expect(mock.writeInitialization(isA(Element), + eq(libraryURLs), + eq(inits))).andAnswer({ + def body = EasyMock.currentArguments[0]; + + body.comment("MM-INIT"); + } as IAnswer).once()*/ + + + return mock; + } + } From 4d47e170b8d2fa8689fda73e5b3ee5d1cf55c99d Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Tue, 29 Jul 2025 21:22:46 -0300 Subject: [PATCH 16/30] Fixing AssetTests.external_url_asset_bindings --- .../tapestry5/integration/app1/AssetTests.java | 8 ++++---- .../integration/app1/services/AppModule.java | 5 +++++ .../integration/app1/pages/nested/AssetDemo.js | 16 ++++++++++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java index 06a92458ab..7cb10e5e5f 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java @@ -52,10 +52,10 @@ public void external_url_asset_bindings() { openLinks("AssetDemo"); - assertEquals("http://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.js", getText("httpAsset")); - assertEquals("https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.js", getText("httpsAsset")); - assertEquals("http://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.js", getText("protocolRelativeAsset")); - assertEquals("ftp://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.js", getText("ftpAsset")); + assertEquals("http://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.js", getText("httpAsset")); + assertEquals("https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.js", getText("httpsAsset")); + assertEquals("http://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.js", getText("protocolRelativeAsset")); + assertEquals("ftp://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.js", getText("ftpAsset")); // check whether externaly @Import'ed d3 works assertTrue(isElementPresent("css=svg")); diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java index b5c7017cc0..173707245c 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java @@ -191,6 +191,11 @@ public static void contributeApplicationDefaults(MappedConfiguration run(d3)); +} \ No newline at end of file From 273babbcb70becae67488eab23bbb7cbf782eb00 Mon Sep 17 00:00:00 2001 From: "Thiago H. de Paula Figueiredo" Date: Sat, 2 Aug 2025 00:49:35 -0300 Subject: [PATCH 17/30] TAP5-2810: hopefully final ES module import and init fixes --- .../internal/services/DocumentLinkerImpl.java | 18 +++-- .../services/EsModuleInitsManager.java | 32 ++++++-- .../ajax/EsModuleInitializationImpl.java | 11 +++ .../javascript/EsModuleManagerImpl.java | 76 ++++--------------- .../services/javascript/EsModuleManager.java | 12 +-- .../main/typescript/src/t5/core/pageinit.ts | 8 +- .../JavaScriptSupportAutofocusTests.groovy | 2 +- .../integration/app1/EsModuleTests.java | 1 - 8 files changed, 76 insertions(+), 84 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java index fd885ce877..41f8ef1a8c 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DocumentLinkerImpl.java @@ -24,6 +24,7 @@ import org.apache.tapestry5.services.javascript.ModuleManager; import org.apache.tapestry5.services.javascript.StylesheetLink; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -38,7 +39,7 @@ public class DocumentLinkerImpl implements DocumentLinker private final ModuleInitsManager initsManager = new ModuleInitsManager(); - private final EsModuleInitsManager esModulesinitsManager = new EsModuleInitsManager(); + private final EsModuleInitsManager esModulesInitsManager = new EsModuleInitsManager(); private final List moduleConfigurationCallbacks = CollectionFactory.newList(); @@ -154,11 +155,11 @@ public void updateDocument(Document document) addElementBefore(head, existingMeta, "meta", "name", "generator", "content", tapestryBanner); } - final List esModuleInits = esModulesinitsManager.getInits(); - if (isHtmlRoot && !esModuleInits.isEmpty()) + final List imports = esModulesInitsManager.getImports(); + if (isHtmlRoot && !imports.isEmpty()) { esModuleManager.writeImportMap(root.find("head"), esModuleConfigurationCallbacks); - esModuleManager.writeImports(root, esModuleInits); + esModuleManager.writeImports(root, imports); } addScriptElements(root); @@ -271,14 +272,17 @@ protected void addContentToBody(Element body) "src", url); } + // Write the initialization at this point. if (requireJsEnabled) { - // Write the initialization at this point. moduleManager.writeInitialization(body, libraryURLs, initsManager.getSortedInits()); + + // Libraries were already added in the line above. + esModuleManager.writeInitialization(body, Collections.emptyList(), esModulesInitsManager.getInitsAsJsonArrays()); } else { - esModuleManager.writeInitialization(body, libraryURLs); + esModuleManager.writeInitialization(body, libraryURLs, esModulesInitsManager.getInitsAsJsonArrays()); } } @@ -350,7 +354,7 @@ public void addEsModuleConfigurationCallback(EsModuleConfigurationCallback callb public void addEsModuleInitialization(EsModuleInitialization initialization) { assert initialization != null; - esModulesinitsManager.add(initialization); + esModulesInitsManager.add(initialization); hasScriptsOrInitializations = true; } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java index 96a01f1d0d..c79201b5c4 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EsModuleInitsManager.java @@ -39,27 +39,39 @@ public class EsModuleInitsManager { private final Set modules = CollectionFactory.newSet(); + private final List imports = CollectionFactory.newList(); + private final List initializations = CollectionFactory.newList(); public void add(EsModuleInitialization initialization) { assert initialization != null; - // We ignore a module being added again. - final String moduleName = ((EsModuleInitializationImpl) initialization).getModuleName(); - if (!modules.contains(moduleName)) + // We avoid having the same module being imported more than twice. + // Also notice non-pure inits (i.e. ones having a function name or + // both) are added both to the imports, so they can have + //