Design: Skin Extensions (SX)
JavaScript Extensions (JSX), StyleSheet Extensions (SSX)
JSXs and SSXs are stored as standard XObjects. Usually, an Application or an Interface Component will have one or more XClasses, with their templates and sheets, some navigational documents with scripts processing the structured documents, an internationalization bundle document, some interface extensions and some skin extensions. A document can store more than one skin extension, but they are not separable, meaning that when an application requests for a JSX, it uses that extension's document name, and all the JSX objects found in that document are sent to the client in one response. The benefit of having more than one extension in a document is that the code can be separated into distinct "files" (=objects), and the server compacts all those files into one response. Reasons why we shouldn't differentiate objects in the same document:- Simpler URLs; using the object number would require an extra query parameter
- Simpler extension identification; a properly chosen document name is stable, while the object number can be changed and has no meaning
- If we need more extension, we can just use more documents, it's not like we're running out of available document names
JSX/SSX class
- name: string
- code: textArea
- use: staticList: onDemand, always ([alternative] global: boolean: yes, no)
- parse: boolean: yes, no [optional, to be decided]
- cache: boolean: yes, no ([alternative] staticList: long, short, never, forbid) [optional, to be decided]
Implementation details
Since the javascript and css import declarations must be in the html header, and the extensions are pulled while generating the html content, it is obvious that the import statements must be gathered during the rendering process, and inserted afterwards. The gathering process can be done using a process similar to the TOC generation. During the rendering phase, we gather the pulled extensions in a list (actually an ordered set, since an extension can be pulled in more than once, and we don't want to include it twice in the header; and really there are two sets, one for each type of skin extension, scripting or styling). Two new classes, JsxHelper and SsxHelper should be created in com.xpn.xwiki.util, and added to the velocity context in the prepareContext phase (com.xpn.xwiki.render.XWikiVelocityRenderer#prepareContext(XWikiContext)). Extensions can be pulled in two ways:- An imperative one, using velocity. Something like $jsx.use("XWiki.AjaxTable"), $ssx.use("xwiki:Panels.PanelWizard"). These calls will add the parameters to the respective extension lists. As an example, inside the Panels.PanelWizard document content we would put $jsx.use("Panels.PanelWizard").
- And a declarative one. We can extend the IX objects with two more fields, "jsx" and "ssx", which declare a comma-separated list of SXs that are needed whenever this IX is used. Later, when the IX controller detects that an IX must be used, it also adds its SX requirements to the lists.
"<script type=\"text/javascript\" src=\"" + xwiki.getURL(jsxDocName, "jsx") + "\"></script>\n"; "<link href=\"" + xwiki.getURL(ssxDocName, "ssx") + "\" rel=\"stylesheet\" type=\"text/css\"/>\n"
Usage
Since Skin Extensions are much easier to implement than Interface Extensions, they should be implemented as soon as possible and used in templates and XWiki documents. This raises the question what should be a standard skin feature, and what should be an optional component. If we move features outside skins, this will mean that they will require their own Jira projects, build modules and release cycles. This can be seen as an advantage, as we don't have to release a new version of XE for a change in a skin feature. But until we have an easy application update process, this can also be seen as a disadvantage, as admins will usually install only new versions of XE, and not of individual apps. All the javascript files, except prototype, and all the custom css files (like usersandgroups.css, lightbox.css) should be removed from the skin and placed in SkinExtensions, packaged as Applications bundled in products. This will allow reusable skin features without forcing them to be part of a skin or of the platform templates, and to be used in older version of XWiki without upgrading the whole skin (not older now, but versions that will support skin extensions and will be old in the future). If a SX is general, it should be posted in its own document. If it is particular to a document in an application (like the PanelWizard script), it should be attached to the page using it. Open question 1: Should $jsx.useFile("filename.js") work for files located on the disk? This allows the same pull process to be used with files located in the skin, without requiring SX documents and objects. I'd say yes. Then, what should the URL look like? /xwiki/bin/jsx/skins/albatross/somestyle.css is OK? Open question 2: This also affects IX. How to specify dependencies? An extension could list some other extensions as requirements, and when using it, also add its dependencies to the list of used extensions.Remarks
It is generally accepted that one css/js file is better than more. However, this is not possible in the current model. We could combine all the files in one response, but that would mean that:- We either send all the possible css/js files at once, which means one huge file with all the code users might not need. This was the initial goal, not to globally send useless code to users. This also means that whenever we add a new component at runtime, the whole collection of js/css code must be sent back to the client, although the new code could be just a small file, and the already existing large collection of code could be pretty large.
- Or we send one large file with all the needed components in the current page. This means that several files would be sent more than once, and just because the current document needs one small new js, the client will have to receive all the files already cached.
- Or we send one large file with essential code, and we keep the SX model for optional extensions. In a way, this is what the proposal does, considering that the skin should only need prototype (or another library we might choose) as the essential code. On the CSS part, the core files should also be packed in one response, leaving only optional component styling outside.
Version 3.5 last modified by Sergiu on 11/03/2008 at 00:30
Document data
Attachments:
No attachments for this document
Comments: 0