diff --git a/doc/liza.css b/doc/liza.css index 623bb28..0990fe9 100644 --- a/doc/liza.css +++ b/doc/liza.css @@ -125,9 +125,7 @@ but don't sacrifice space on small screens. border-color: #d33682; } -.doc-notice.devnotice p::before, -.doc-notice.devnote p::before, -.doc-notice.tip p::before +.doc-notice p::before { display: block; float: left; @@ -151,9 +149,12 @@ but don't sacrifice space on small screens. content: '\261E'; } -.doc-notice.devnotice p, -.doc-notice.devnote p, -.doc-notice.tip p +.doc-notice.trivia p::before +{ + content: '\1F914'; +} + +.doc-notice p { padding-left: 1.75em; } diff --git a/doc/macros.texi b/doc/macros.texi index 117786d..ed7dbfd 100644 --- a/doc/macros.texi +++ b/doc/macros.texi @@ -150,6 +150,18 @@ This system has maintenance concerns. @end macro +@c Conveying the historical details of the project is important to +@c understand why the system exists in the state that it does +@c today. Use of this macro will hopefully help mitigate some of the +@c problems noted by Peter Naur in his paper Programming as Theory Building: +@c http://pages.cs.wisc.edu/~remzi/Naur.pdf +@macro trivia{text} +@noticestart{trivia} +\text\ +@noticeend +@end macro + + @c link to source file if URI is known, otherwise display @c the path to the file @ifset SRCURI diff --git a/doc/program.texi b/doc/program.texi index a806822..248b703 100644 --- a/doc/program.texi +++ b/doc/program.texi @@ -45,6 +45,7 @@ Programs are ideally compiled from a @ref{Program XML,,Program@tie{}XML} @end menu + @node Program UI @section Program UI @maintenance{ @@ -101,11 +102,13 @@ It also displays question help text (also configured through the XML) and any error messages (@pxref{Error Handling}). @menu -* Group Styles:: Different ways of displaying groups of questions to - the user. +* Group Styles:: Different ways of displaying groups of questions + to the user. +* DOM Abstraction :: Representing and efficiently manipulating the DOM. @end menu + @node Group Styles @subsection Group Styles @refactor{ @@ -187,6 +190,104 @@ A list of available styles is detailed in @ref{t:group-styles}. +@node DOM Abstraction +@subsection DOM Abstraction +@cindex DOM +@cindex Dojo, History +@cindex jQuery +@devnotice{jQuery is still used throughout parts of the framework and + is a performance bottleneck@mdash{ + }it needs to be fully removed and replaced with this + DOM@tie{}abstraction.@footnote{ + See @srcrefraw{src/ui/ElementStyler}.}} + +@trivia{Liza was conceived long before frameworks like React existed. + The implementation originally used Dojo because of its + broad widget set, + but it was later dropped because of extreme performance issues, + especially on the browsers of the day + (Liza had to support Internet Explorer@tie{}6!); + at one point, + certain steps took over a minute to load for the + most unfortunate of users. + jQuery was then used for various parts of the UI and for ease of + DOM manipulation, + because of the lack of good and universal DOM APIs back then. + It too became a bottleneck. + Using DOM@tie{}APIs is now easy with modern browsers.} + +Liza's DOM abstraction contains a couple of components: + +@cindex DOM, Context +@cindex DOM, Field +@itemize +@item @dfn{DOM Fields} represent a field on the DOM. + Each field has a name and an index associated with the + DOM@tie{}node. + Nodes are cached in memory after first access and queue requests + during lookups to prevent stampeding. + Provides basic DOM operations, + including styling, containing row, and parent/sibling selecting. + See @srcref{src/ui/field}. +@item @dfn{DOM Context} is a slice of the DOM used for restricting queries. + It can attach and detach sections of the DOM, + and be further split into a context hierarchy. + The @srcrefjs{src/ui/context,DomContext} provides field querying + (see @srcrefjs{src/ui/field,DomField}) and caching. + See @srcrefraw{src/ui/context}. +@end itemize + +@tip{It is important to always use these abstractions for any portions + of the DOM under control of this abstraction; + otherwise, assumptions used for caching may result in + unintended behavior.} + +Using DOM contexts, + DOM operations can be restricted to small windows (for example, + groups or tabs), + further reducing the impact of DOM queries. + +The @dfn{root context} is represented by + @srcrefjs{src/ui/context,RootDomContext}@mdash{ + }sub-contexts can be obtained by invoking @jsmethod{slice} on any + context, + which creates a new context from a subset of the parent. +Detaching a parent will detach all child contexts. + +Contexts can be manipulated in memory before being re-attached. +Detach a context from the DOM with @jsmethod{detach}, + and attach with@tie{}@jsmethod{attach}. +A context is aware of its parent and will re-attach itself to the DOM + in the correct place. +A child context always attaches to the parent, + and so will not be rendered until the parent attaches. + +@tip{Always detach from the DOM before performing extensive manipulations; + this prevents the need for expensive re-painting until + manipulation is done, + at which point the context can be re-attached.} + + +@menu +* Field Styling:: Styling @srcrefjs{src/ui/field,DomField}. +@end menu + + + +@node Field Styling +@subsubsection Field Styling +@cindex Field, Styling +@helpwanted + +@srcrefjs{src/ui/field,DomField} is styled using field stylers + (see @srcrefraw{src/ui/styler}). +The two most notable stylers are + @srcrefjs{src/ui/field/ErrorFieldStyler} and + @srcrefjs{src/ui/field/NaFieldStyler}, + which style fields in error and hide fields that are no applicable + respectively. + + @node Program XML @section Program XML @helpwanted @@ -301,6 +402,7 @@ There is no limit to the number of groups that can share the same link. fields in the link.} + @node Specifying Predicates @subsection Specifying Predicates diff --git a/src/ui/ElementStyler.js b/src/ui/ElementStyler.js index 9442b1f..fd88463 100644 --- a/src/ui/ElementStyler.js +++ b/src/ui/ElementStyler.js @@ -452,7 +452,13 @@ module.exports = Class( 'ElementStyler', { change_event = ( change_event === undefined ) ? true : change_event; - var $element; + // just to be sure before we fully remove this + if ( change_event !== false ) + { + throw Error( + "ElementStyler#setValueByName change_event is being removed" + ); + } // set value switch ( this._getElementType( name ) ) @@ -486,35 +492,19 @@ module.exports = Class( 'ElementStyler', var i = elements.length; while ( i-- ) { - var $question = $( elements[ i ] ); - - if ( $question.attr( 'value' ) == value ) - { - $question.attr( 'checked', true ); - $element = $question; - } - else - { - $question.attr( 'checked', false ); - } + const question = elements[ i ]; + question.checked = ( question.value === ''+value ); } break; default: - $element = this.getElementByName( + const $element = this.getElementByName( name, index, null, $context ); $element.val( ''+( value ) ); } - // the autochange propery signifies that we should trigger the - // change event - if ( $element !== undefined && ( change_event || ( $element.data( 'autochange' ) === true ) ) ) - { - $element.trigger( 'change' ); - } - return this; },