TO DO - General
- Contents
- 1. Split UIZE Core and UIZE Web Site
- 2. Migrate Modules Into Deeper Namespaces
- 3. Localization
- 4. Eliminate All Extension Modules
- 5. Code Size Optimization
- 6. Folder Organization of JavaScript Modules
- 7. Eliminate Private Methods As Properties
- 8. Possible New Namespaces
- 9. Code to Factor Out
- 10. General Drag and Drop Framework
- 11. Iterator Interface
- 12. Safe Privates
- 13. Rethinking Module Definitions
-
14. Version Support in Module Mechanism
- 14.1. Requirements
- 14.2. - possible approach...
- 14.3. - declaring information about a module variation
- 14.4. - requiring specific versions of modules...
- 14.5. - requiring a variation of a module using a custom match...
- 14.6. In a Nutshell
- 14.7. Ideas
- 14.8. Module Registry
- 14.9. Module Meta Data
- 14.10. Loading of Modules
- 14.11. Module Variations
- 14.12. Module Specifier
- 14.13. Variation Qualifier
- 14.14. Issues to Address
- 15. JavaScript Language Unit Tests
- 16. Function Argument Validation
- 17. Client Services
- 18. Test Improvements
- 19. Node as a Widget
- 20. Test Modules to Implement Next...
- 21. Value Transformer Function
- 22. Profiling Support
- 23. Performance Questions...
- 24. Idea for Handling Functions That May Be Asynchronous
- 25. - the most challenging and difficult problems to solve
- 26. Generic Solution for Tracking
- 27. Only Change Property Values Using the set Method
- 28. - factor out code for validating a value, where the validator can be...
- 29. - should factor out code to set HTML for frame or iframe (best implementation so far in Simple Doc Tester)
- 30. Key Aspects of a Framework
- 31. - Uize.Delayed
- 32. More Unobtrusive JavaScript Features
-
33. Code Development (in order of priority)
- 33.1. **** property changed events refactoring
- 33.2. - high-minded ideas
- 33.3. - memory usage optimizations
- 33.4. - performance optimizations
- 33.5. widget ID scheme refactoring
- 33.6. image border fade overlay
- 33.7. Graphing DHTML
- 33.8. Image Letters Widget (Uize.Widget.Spritext)
- 33.9. Auto-wired hide-reveal mechanism
- 33.10. Stars Rating Widget
- 33.11. Utility routine to easily make a bunch of links open in a new window
- 33.12. Auto-viewer for Images
- 33.13. IDEAS
- 33.14. Puzzle Game
- 34. BUGS
This document lists to do tasks that relate to the UIZE JavaScript Framework in general - not relating to the documentation or the site (see TO DO - Documentation), or specific JavaScript modules (see TO DO - Modules).
1. Split UIZE Core and UIZE Web Site
Split out the UIZE core and UIZE Web site code into two separate repositories.
the UIZE Web site will build like any other site that uses UIZE | |
after the split, there will be two repos - one for the UIZE JavaScript Framework and one for the UIZE Web aite | |
the split should be done in such a way as the full revision history is retained for files in both of the two new repos | |
perhaps this would be the right time to create a new "uize" project for the core (as opposed to the current and slightly clumsy "UIZE-JavaScript-Framework"), so then we could have: uize and uize-site |
1.1. Update Documentation
Documentation in the UIZE site should be updated appropriately to reflect the new structure, including...
setup reference docs | |
reference docs that mention that the repo contains a full Web site with examples |
1.2. Improved Development Discipline
Creating a separate repo for the Web site would reinforce discipline around how changes to the UIZE core are made.
It would no longer be so easy to make changes across the core and Web site code in a single refactoring operation. So, treating the UIZE Web site as a true user of the framework, separate from the core code, will help to encourage more thoughtful evolutionary changes to the framework.
REFERENCE
https://confluence.atlassian.com/bitbucket/split-a-repository-in-two-313464964.html |
2. Migrate Modules Into Deeper Namespaces
Migrate some modules at the root Uize
namespace into deeper namespaces in order to tuck them away.
Uize.Tooltip -> ??? Uize.Xml -> ???
3. Localization
3.1. Defaulting Missing Translations
3.1.1. Questions
is it appropriate to default missing strings to the primary language counterparts, or should they remain empty? | |
if missing strings are defaulted, should they be wrapped in some way to indicate that they have been defaulted, for the sake of development, QA testing, linguistic QA, etc. | |
should defaulting / fallback be a configurable behavior? | |
should the defaulting approach be different for different environments / situations (dev vs production)? | |
should defaulting be handled by a project's own build process, or can it be handled by the localization service? Is defaulting a service that can be made available to a project's own build process? |
3.1.2. Ways to Handle Missing Strings
they remain blank | |
they are defaulted to the key | |
they are defaulted to the primary language value (e.g. fr-CA -> en-US) | |
they are defaulted to a fallback language's value (e.g. fr-CA -> fr-FR) | |
they are defaulted using a cascade of fallback languages' values, which may include the primary language (e.g. fr-CA -> fr-FR -> fr-CH -> en-US) | |
they are optionally wrapped with an indicator that they have been defaulted |
3.2. System for Discouraging Concatenation
Develop an approach that discourages developers from concatenating resource strings together.
At the very least
make it hard / uncomfortable to perform concatenations (force the developer to jump through some hoops) | |
make it easy to scan for concatenations using static analysis | |
produce warnings or errors at runtime when concatenations are performed in lazy ways |
The basic principle of the proposed approach is that the binding logic that displays text to the user by binding data to the UI requires that the strings be "blessed" by localization code.
3.2.1. Resource strings, as obtained from the resource files, are "blessed".
Represented as functions, blessing the resource strings would involve assigning a special property on the functions that the UI binding code would check for.
3.2.2. Resource strings cannot be programmatically concatenated
This is accomplished by making the strings functions, so that concatenating the functions would invoke the toString implicit method to serialize the functions and it would be obvious immediately to the developer that they can't do that.
Similarly, attempting to call String methods on the resource strings would fail. The only way to fool the system would be to call the functions, concatenated the results, and then wrap the result in a function that returns that result and is blessed by assigning the special property on the function. This is something that would be easy to scan for in static analysis of the code, so it would be easy to audit the code for cheats.
3.2.3. Compile-to-objects Approach
When the resource strings are compiled, they are compiled to objects that implement the valueOf and toString methods in such a way as to throw an error when invoked - this is to discourage simple use of resource strings in expressions like concatenations.
In order to use a resource string in an expression, the developer would have to call a specially named method on the resource string, and such instances in the code would be easy to detect for during static analysis of the code. This makes it much easier to audit the code in order to review cases where resource strings are being used in ways that are recommended against.
EXAMPLE
var resourceStringsInExpressionsBehavior = 'error'; // 'error' | 'warning' | '' function ResourceString (value) { this.value = value; } ResourceString.prototype.valueOf = ResourceString.prototype.toString = function () { if (resourceStringsInExpressionsBehavior == 'error') { throw new Error ('Resource strings must not be used directly in expressions.'); } else { if (resourceStringsInExpressionsBehavior == 'warning') { console.warn ('Resource strings must not be used directly in expressions.'); } return this.value; } }; ResourceString.prototype.resolveResourceString = function () { return this.value; }; var stringA = new ResourceString ('This is a '), stringB = new ResourceString ('resource string') ; stringA + stringB;
3.2.3.1. Implications
There are two ways that a developer can use a resource string...
they can pass a reference to the resource string to a method that will process the resource string by calling the appropriate method on the string object | |
they can call the appropriate method on the string object themselves in order to resolve it |
Generally, the developer should not often need to explicitly resolve resource strings themselves, since this should be centralized in the framework's binding mechanism.
4. Eliminate All Extension Modules
Eliminate all extension modules and replace them with either helper objects containing purely static methods or mixin modules.
4.1. Extensions as Anti-Pattern
Extension modules, like plugins, can be considered an anti-pattern because loading them has side effects on other loaded modules.
In that sense, they are not isolated. The side effects of loading an extension module are deliberate and are their one benefit, but this pattern causes several problems...
4.1.1. Interoperability Issues
Extension modules can cause problems of interoperability, since shared modules are affected.
The effect is much like that of setting global variables or modifying the prototypes of built-in objects. There is no guarantee that two different extension modules won't have interactions / conflicts.
4.1.2. Dependency Management Issues
Extension modules can cause problems of dependency management in two key ways...
4.1.2.1. Dependencies are Unclear from Code
Since an extension module affects an object or class other than itself, it can be unclear from viewing code that the code has a dependency on an extension module.
There may be a call to an instance method of an instance of a class, but that method may not be defined in the class but actually in an extension module that modifies the class.
4.1.2.2. Modules Use Stuff That is Found Lying Around
Since an extension module modifies a class or object that is shared and may be in use by other modules, that modified class or object may then appear to have features to the code in other modules that that code isn't aware is defined in an extension.
Code in other modules may just discover instance methods (through an IDE's code auto-complete feature, for example) that are "lying around" and start using them, without realizing that the dependency on the extension module also needs to be declared in the module's code. Then, if that module is used in a different context where the extension module hasn't been required, the module's code can fail at runtime under some specific condition where the methods that would have been added by the extension module are called.
4.1.3. Code Analysis Problems
Since extension modules affect objects or classes other than themselves, they can cause problems for code analysis because they can make modules appear to define more features than they actually do.
If a class module is loaded, then an extension module is loaded that modifies that class, and then a subclass module is loaded, if that subclass is examined for features, it will appear to define features that are not actually defined by it, but were in fact defined by the extension module that modified its base class. This can complicate the task of code analysis to perform feature discovery. Features added by extension modules are mis-attributed.
4.2. Extension Modules to Eliminate
The following list of extension modules should be eliminated...
5. Code Size Optimization
5.1. Lightweight JSON Module
Implement lighter weight JSON module.
It's time. There are now enough clients that support the built-in JSON
object that it's time to create a lightweight, non-pretty print serializer and parser for JSON objects that can be used in modules where control over formatting is not needed.
5.2. Improved Module Declaration Semantics
5.2.1. Module Declaration Short Form
Support a new short form for module declaration as an alternative to the object form.
INSTEAD OF...
Uize.module ({ name:'MyNamespace.MyModule', required:[ 'RequiredModule1', 'RequiredModule2' ], builder:function () { // build and return the module } });
USE...
Uize.module ( 'MyNamespace.MyModule', [ 'RequiredModule1', 'RequiredModule2' ], function () { // build and return the module } );
OR...
Uize.module ([ 'MyNamespace.MyModule', [ 'RequiredModule1', 'RequiredModule2' ], function () { // build and return the module } ]);
5.3. Migrate or Remove Features
Look at migrating some lesser used features out of core modules into new / other modules.
5.3.1. Uize.Widget
Specific features to be decided.
5.4. Tougher to Accomplish
6. Folder Organization of JavaScript Modules
Support the ability to have JavaScript modules optionally organized inside folders, rather than all being in a flat list inside a single folder.
6.1. Proposed Solution
A means will be provided to specify one or more namespaces, under which all modules will be organized by folder rather than in a flat list.
For all modules not under the specified folder organization namespaces, modules will be organized in a flat list. It will not be possible to mix different organization types at different levels along a module namespace path. For, for instance, if all modules under the namespace Foo.Bar
are configured to be organized by folder, then it will not be possible to have modules under the namespace Foo.Bar.Baz.Qux
be organized by flat list.
6.1.1. Folder Organization is the Future
It is believed that folder organization of all modules in a codebase is generally the preferred approach going forward.
Therefore, supporting folder organization only for configured namespaces is primarily a migration strategy in order to support backwards compatibility with existing codebases while the migration is performed over time.
6.1.2. Finding Modules
All places in the code where locations of modules are calculated will need to be updated in order to support configurable folder organization of modules under specific namespaces.
Some of the places that will be affected include...
6.1.2.1. Dynamic Module Loader
.
6.1.2.2. File Builder
.
6.1.3. Module Resources
Modules that have dependencies on resources such as images and want to generate paths to those resources inside their implementation code should use the Uize.Class.pathToResources
property.
The value of the Uize.Class.pathToResources
property is calculated for each module to take into account whether or not the module is under a namespace that uses folder organization.
7. Eliminate Private Methods As Properties
In all modules in the UIZE codebase, eliminate all private instance and static methods in favor of functions that get passed the context as the first argument.
NOTES
update guide documentation on classes, inheritance, feature declaration, and such to no longer recommend defining private methods as "_" prefixed properties on the instance or class | |
there will be a few cases where private instance methods are provided as onChange handlers for state properties - these will have to be left as is, and if they are also called on this , then they will have to be called using the call or apply methods |
8. Possible New Namespaces
Uize.Crypto |
9. Code to Factor Out
Code to factor out of widget classes into non-Web specific modules...
Uize.Widget.Drag - the Uize.Widget.Drag.insertShield and Uize.Widget.Drag.resizeShield static methods should maybe be factored out into a generic shield module (possibly a package module under the Uize.Dom namespace) |
|
Uize.Widget.ImagePort - the Uize.Widget.ImagePort.getScaledRect and Uize.Widget.ImagePort.getSizingAndAlign static methods have been factored out into the Uize.Math.LogicalPos module, so they should now be deprecated in the Uize.Widget.ImagePort module |
|
Uize.Widget.Population - pure population string functionality should be migrated into a new Uize.Str.Population module (or Uize.Template.Population ?) |
|
Uize.Widget.Tree - model stuff for the data structure that represents a tree (Uize.Data.Tree ?) |
Solve the problem of some widget classes being used only for their static methods and not their full functionality. Perhaps some helper method functionality should be separated out into packages that can be used outside of the context of widgets. As a general rule, if some functionality might be useful without widgets being involved, then it should be implemented in a separate package module.
Uize.Widget.Page - the Uize.Widget.Page.launchPopup static method |
10. General Drag and Drop Framework
Virtual dom events to mimic new HTML5 dnd events.
11. Iterator Interface
11.1. Instance Methods
first - rewinds the iterator to the beginning and returns the value for the first iteration |
|
next - advances to the next iteration and returns the value for the iteration |
|
advance - advances to the next iteration, if possible, and returns a boolean indicating success |
|
rewind - rewinds the iterator, so that calling next will return the value for the first iteration |
|
reset - synonymous with rewind |
|
forEach - executes the entire iteration, calling the specified iteration handler function for each iteration |
|
end - ends the iteration, after which calling hasNext will return false |
11.2. Instance Properties
value - the value for the current iteration |
|
current - synonymous with value |
|
key - the key / index for the current iteration |
|
ended - a boolean, indicating whether or not the iteration has ended |
11.3. Usages
EXAMPLE
for (var currentSum = series.first(); !series.ended; currentSum = series.next();) { // do stuff }
EXAMPLE
var currentSum; while (series.advance()) { currentSum = series.value; // do stuff }
11.4. Uize.Iterator
The Uize.Iterator
object implements the iterator interface.
11.4.1. As a Wrapper
A Uize.Iterator
object can be instantiated as a wrapper around a number of different source values.
11.4.1.1. Wrapping a Number
.
11.4.1.2. Wrapping an Array
.
11.4.1.3. Wrapping an Object
.
11.4.1.4. Wrapping a JavaScript Iterator
.
11.4.2. Modules Supporting Iterators
Several UIZE modules provide built-in support for the Uize.Iterator
class.
For example, the Uize.Fade
module supports wrapping a Uize.Fade
value series as an iterator.
Uize.Data.Combinations |
|
Uize.Str.Lines |
|
Uize.Color.Util |
|
Uize.Curve |
11.4.3. Iterator Modifier Function
A function, in the spirit of a curve function modifier, that returns an iterator function that is a modification of the source iterator function (or the source resolved to an iterator function).
An iterator modifier function can accept a source that is not an iterator object, which will first be resolved to an iterator object.
Examples of iterator modifier functions...
Uize.Iterator.map - the new iterator produces every value from the source iterator, transformed through a specified value transformer |
|
Uize.Iterator.filter - produces an iterator with only those iterations whose values pass the specified value matcher |
|
Uize.Iterator.stretch - stretches out the number of iterations by either repeating or interpolating between the values in the original iterator function |
|
Uize.Iterator.slice - produces an iterator that includes only the specified slice of iterations from the source iterator |
|
Uize.Iterator.sliceByMatch - produces a slice of the source iterator, where the first and last iterations are determined by specified value matchers |
|
Uize.Iterator.concat - produces a new iterator by concatenating multiple iterators |
|
Uize.Iterator.conflate - takes multiple source iterators and produces a new iterator where the value for each iteration is generated by the specified function that takes the values from all the source iterators as its input |
|
Uize.Iterator.nest - produces an iterator where for each iteration of an outer source iterator, an inner "nested" iterator is iterated over, where the inner iterator is provided the value of an iteration of the outer iterator as a seed value, and where the resulting iterator returns the values from the nested iterator |
|
Uize.Iterator.cycle - produces an iterator where the specified source iterator is cycled through the specified number of times |
|
Uize.Iterator.interleave - produces an iterator by interleaving multiple source iterators together (i.e. where each iteration alternates through the specified source iterators) |
12. Safe Privates
A new system to support safe private properties and methods - for both instance as well as static private properties and methods.
EXAMPLE
/*** create variables for mapping between pretty name of privates and generated names ***/ var _myProperty = _class.priv ('myProperty'), _myMethod = _class.priv ('myMethod') }); _class.declare ({ instanceProperties:Uize.pairUp (_myProperty,'initial value'), instanceMethods:Uize.pairUp (_myMethod,function () {/* method implementation */}) }); alert (this [_myProperty]); // accessing instance property this [_myMethod] (); // calling instance method
ISSUES
all accesses and assignments of privates involve an additional variable lookup step, and dereferencing with the result of an expression (so, some precompilation benefits are lost) | |
"lookup" of private names involves accessing variables in a higher scope than point of access, so performance cost | |
names must be generated for all privates, which adds setup code | |
makes it a little bit harder to do impromptu assignments of privates | |
accessing state properties via their private names becomes rather inconvenient, and their private names must be registered in an extra step |
QUESTIONS
how does one provide private names for state properties? Provide an example. | |
should multiple calls to the priv method on the same class for the same named private always return the same result? If so, this would suggest that external code could probe around and easily discover the internal name for a private. |
|
how is the mapping / registry of privates for a class inherited by a subclass? Does it have to be? |
13. Rethinking Module Definitions
13.1. Using a Provide-Need Pattern as Foundation
13.1.1. In Essence
the needer specifies the criteria for what is needed | |
from the registered providers, a provider is selected that can best satisfy the need | |
the chosen provider sees if anything is already cached that matches the need | |
if something is cached, the provider passes it on to the needer | |
if nothing is cached, then... | |
the provider goes about obtaining something that matches the need | |
the provider caches the thing that it obtained, for the benefit of others that may have matching needs | |
the provider passes the needed item on to the needer |
13.1.2. Other Patterns That Can Be Modeled on Provide-Need
13.1.2.1. Needing an Instance
An instance provider would be given criteria for the instance needed, such as class module, initial / constructor properties, etc.
The provider registered for providing instances would in turn need the class module for a specific instance that is needed.
13.1.2.2. Needing a Session
Code that needs the user to be logged in and to have a session could use a provide-need pattern, where a registered provider for session would go about prompting the user to log in.
Uize.provide ( {type: 'module', org: 'Uize'}, function (needed, deliver) { // code here to load Uize modules } );
Uize.provide ( {type: 'module', org: 'MyCompany'}, function (needed, deliver) { // code here to load MyCompany modules } );
Uize.providers
Uize.need ( {type: 'module', org: 'Uize', name: 'Uize.Widget'}, function (Uize_Widget) { // now we can use the Uize.Widget class } );
13.2. Uize.module And Uize.require as Syntactical Sugar
Uize.module ({ org: 'MyCompany', name: 'MyCompany.Foo.Bar', builder: function () { // build and return the module } });
Uize.require ( { org: 'MyCompany', name: 'MyCompany.Foo.Bar' }, function (MyCompany_Foo_Bar) { } );
Uize.require( 'MyCompany:MyCompany.Foo.Bar', function () { } );
14. Version Support in Module Mechanism
14.1. Requirements
14.1.1. Support the following...
ability to have multiple versions of UIZE on the same page | |
ability to have different versions of the same module on the same page | |
ability for modules to have dependencies on specific versions of other modules | |
it must still be possible for a module to declare itself and then be available as a required module for other modules that are subsequently declared in the same scope |
14.2. - possible approach...
VARIATION 1
Uize.module ({ name:'MyNamespace.MyModule', required:[ 'Uize.Curve', // end up getting version 2, let's say 'Uize.Curve.Mod' // end up getting version 3, let's say ], builder:function (_superclass,m) { // referencing required modules inside this scope... m ('Uize.Curve')... m ('Uize.Curve.Mod')... } });
VARIATION 2
Uize.module ({ name:'MyNamespace.MyModule', required:{ _Uize_Curve:'Uize.Curve', // end up getting version 2, let's say _Uize_Curve_Mod:'Uize.Curve.Mod' // end up getting version 3, let's say }, builder:function (_superclass,m) { // referencing required modules inside this scope... m._Uize_Curve ()... m._Uize_Curve_Mod ()... } });
14.3. - declaring information about a module variation
EXAMPLE
Uize.module ({ name:'Uize.Widget.FooBar', version:'2', builder:function (_super,m) { // build the module } });
14.4. - requiring specific versions of modules...
VARIATION 1
Uize.module ({ name:'MyNamespace.MyModule', required:[ 'Uize.Curve[2]', // require version 2 of this module 'Uize.Curve.Mod[3-]' // require version 3 or higher of this module ], builder:function (_superclass,m) { // referencing required modules inside this scope... m ('Uize.Curve[2]')... m ('Uize.Curve.Mod')... } });
VARIATION 2
Uize.module ({ name:'MyNamespace.MyModule', required:{ _Uize_Curve:'Uize.Curve[2]', // require version 2 of this module _Uize_Curve_Mod:'Uize.Curve.Mod[3-]' // require version 3 or higher of this module }, builder:function (_superclass,m) { // referencing required modules inside this scope... m._Uize_Curve ()... m._Uize_Curve_Mod ()... } });
14.5. - requiring a variation of a module using a custom match...
EXAMPLE
Uize.module ({ name:'MyNamespace.MyModule', required:{ _Uize_Curve:{ name:'Uize.Curve', version:/^2.[1|2](\..*)?$/ // require versions 2.1 and subversions, or 2,2 and subversions } }, builder:function (_superclass,m) { // referencing required modules inside this scope... _Uize_Curve ()... } });
14.6. In a Nutshell
modules declare themselves and provide meta data (such as version number) that can be used to uniquely identify / disambiguate the module when requiring the module in other modules | |
when a module requires another module, it can provide additional information over and above the required module's name that can be used to identify the module, should multiple variations of the required module be loaded concurrently | |
when a module is declared, all information identifying the module is stored in a global registry. Different variations of the same named module can be loaded concurrently, and the identifying information |
NOTES
it should be possible to use multiple versions of the framework at the same time | |
two variations of the same named module cannot be loaded in the same scope and have all the same other identifying information (such as version) | |
specifying a version number in the module name string is a shorthand way of specifying both a name and version properties |
14.7. Ideas
it should be possible to load a module and have the module be defined without actually building it | |
it should be possible to determine all the modules that have been loaded | |
building a module should not have side effects outside of the definition of the module, such that building a module but not accessing it can affect the way other code behaves in such a way that other code relies upon those side effects and it becomes impossible to defer the building of the module until the first access to it without breaking code |
14.8. Module Registry
14.8.1. Querying the Module Registry
Discovering the Loaded Modules
14.8.1.1. Getting a Module Profile
After a module has been loaded, the module registry can be queried to obtain a profile for the loaded module - even before the module has been built.
Obtaining a module profile for a module is done by calling the getProfile method on the module registry, as follows...
EXAMPLE
Uize.modules.getProfile ('Uize.Widget.Page');
14.8.1.1.1. Module Profile is Persisted
After a module has been built, it is still possible to query the module registry to get the profile for the module in order to find out more about it.
The name, superclass, required list, version, etc. are all persisted in the module registry. In the case of a class module, the profile for a module is assigned to the moduleProfile
property of the module, so it is not necessary to use the module registry interface. However, in the case of a module where it is not appropriate to stitch in properties (such as data modules), getting a module profile must be done through the module registry interface.
14.9. Module Meta Data
EXAMPLE
Uize.module ({ name:'Uize.Widget.FooBar', version:'2', experiment:'performance-optimized', builder:function (_super,m) { // build the module } });
14.10. Loading of Modules
14.10.1. General principles
it is not the responsibility of the code requiring a module to specify how the module should be obtained | |
when a module is obtained, "where" is merely a qualifier to how, and how the location is specified will depend on the method of retrieval |
14.10.2. Loader Configuration
Configuration of the module loading mechanism is independent of how modules are declared or required and is environment specific.
EXAMPLE 1
Uize.module ({ name:'Uize.Widget.Droplist', superclass:'Uize.Widget', required:{ _Uize_Widget_PopupPalette:'Uize.Widget.PopupPalette[2.*]', _Uize_Widget_FormElement:'Uize.Widget.FormElement' }, builder:function (_superclass,m) { /*** Class Constructor ***/ var _class = _superclass.subclass ( _null, function () { this.addChild ('popupPalette',m._Uize_Widget_PopupPalette ()); this.addChild ('input',m._Uize_Widget_FormElement ()); } ; return _class; } });
EXAMPLE 2
Uize.module ({ name:'Uize.Widget.Droplist', superclass:'Uize.Widget', required:[ 'Uize.Widget.PopupPalette', 'Uize.Widget.FormElement' }, builder:function (_superclass,m) { /*** Class Constructor ***/ var _class = _superclass.subclass ( _null, function () { this.addChild ('popupPalette',m ('Uize.Widget.PopupPalette')); this.addChild ('input',m ('Uize.Widget.FormElement')); } ; return _class; } });
14.11. Module Variations
14.11.1. Variations vs Versions
The system should generally support the notion of a variation of a module, rather than specifically versions.
Versions are a narrower application of the more general variation system.
14.11.2. Variation File Naming
In order for multiple variations of the same module to coexist alongside one another in the file system, the different variations must have different filenames.
While a required module qualifier can provide matching criteria that can be used to test if an existing loaded variation of the module matches the criteria, an explicit fallback must be specified in the event that no matching variation can be found in the currently loaded modules.
14.12. Module Specifier
A module specifier identifies a specific module, by name alone, or by a combination of name and an additional variation qualifier.
14.13. Variation Qualifier
A variation qualifier is a specialized kind of value matcher that is applied to all loaded modules to find a module that matches certain criteria.
14.13.1. Variation Qualifier for Specifying Version
In the simplest form, a variation qualifier can be used to indicate an acceptable version or versions for a module.
EXAMPLE
Uize.module ({ name:'MyNamespace.MyModule', required:{ _Uize_Curve:{ name:'Uize.Curve', version:/^2.[1|2](\..*)?$/ // require versions 2.1 and subversions, or 2,2 and subversions } }, builder:function (_superclass,m) { // referencing required modules inside this scope... _Uize_Curve ()... } });
14.14. Issues to Address
if one has a reference to a module, such as in a tool like the Delve tool, how does one determine what the filename of a module is for the purpose of linking to reference documentation? | |
if a variation qualifier is used to indicate a very specific variation of a module, and if that variation is not yet loaded, how does the module loader mechanism know where to obtain a variation that matches the criteria of the variation qualifier? |
15. JavaScript Language Unit Tests
Implement unit tests for all features of the JavaScript language.
15.1. For In Loops And Undefined Array Elements
In several versions of Microsoft's JScript interpreter, if an array is initialized using the literal syntax (i.e. ['value 1','value 2','value 3']
), then any element whose value is initialized to undefined
will not be encountered in a for...in
loop.
EXAMPLE
var keysHash = {}, myArray = ['foo',undefined,'bar'] ; for (key in myArray) { keysHash [key] = true; } alert (keysHash [1]);
In the above example, the alert
statement would alert the value undefined
in interpreters that exhibit the problematic behavior. This behavior could cause problems when using a for...in
loop to compare arrays to see if their elements and custom properties are identical.
A test can be written for this issue as follows...
TEST
var _hasIssueWithForInLoopsAndUndefinedArrayElements = true; for (var _key in [undefined]) { if (_key == 0) _hasIssueWithForInLoopsAndUndefinedArrayElements = false ; }
16. Function Argument Validation
Provide a facility that can optionally be used for function argument validation.
An easy way to define validator logic that can be used to validate the arguments of a function call. It sounds kind of like a test, so maybe some convenience features to allow easily creating an arguments test.
16.1. Method Signature Validation as Optional Layer
Possibly a way generally to have method signature validation as a separate optional layer of code.
16.1.1. As a Separate Module
Maybe a separate module that can accompany the main module?
16.1.2. Validation Code Inside the Main Module
Maybe the validation code is inside the main module, but can be optimized / compiled out for scrunched production code?
There are some benefits to the validation code being inside the main module's implementation. A traditional approach here would be to have a compiler flag / directive that could affect whether or not the validation is included / enabled.
17. Client Services
unit conversion (possible to implement this on the client side, but might be appropriate to migrate such a service to server side at some stage, and in such a case it would be desirable to allow for such a migration in a way that doesn't heavily impact the code using such a service) | |
myWidget.ajax | myWidget.service | |
Uize.Widget.Page.MySite subclass implements performAjax | |
myWidget.ajax ({action:'convert',fromUnit:'inches',toUnit:''}) |
17.1. Example 1
BEFORE
myWidget.callInherited ('ensureLoggedIn') (function () {});
AFTER
myWidget.ajax ({service:'login'},function () {}); // or... myWidget.service ('login',function () {});
17.2. Example 2
BEFORE
myWidget.callInherited ('enableProfile') (function () {});
AFTER
myWidget.ajax ({service:'enableProfile'},function () {}); or... myWidget.service ('enableProfile',function () {});
17.3. What All Can Services Replace or Do?
17.3.1. - things like ensureLoggedIn & ensure (becomes login and services)
should be something like a login service |
basic unit conversion, localization services, validation services | |
useDialog for probably a good number of existing dialogs that gather input / choices from the user | |
anything asynchronous that requires a callback |
17.4. Dynamically Load Client Code Supporting Service
Look at the example of enableProfile.
Benefit of being able to dynamically load code supporting client side execution of portions of the service.
17.5. Site Specific Registration of Services
SYNTAX
{ service:serviceNameSTR, module:moduleNameSTR, methodName:methodNameSTR }
EXAMPLE - EXISTING CODE
{ service:'isValidHandle', module:'MySite.Validator', methodName:'handle' }
EXAMPLE - NEW CODE
{ service:'enableProfile', performService:function (_params,_directives) { var m = this; Uize.require ( 'MySite.Services.MemberProfile', function () { MySite.Services.MemberProfile.enable (_params,m); } ); } }
17.6. Caching?
If implemented in Uize.Comm, can get the benefit of its client caching mechanism. How could one get that benefit using a different approach? (such as implementing in performAjax in page widget subclass)
18. Test Improvements
18.1. Put Tests Into Separate Uize.Tests Namespace
It would be better for tests to exist under a different namespace than Uize.Test
, so that the Uize.Test
namespace can be reserved for test subclasses for special types of tests.
18.2. Clean Up Tests
18.2.1. Make Performance Tests Legit
Performance tests that aren't browser dependent should be turned into true test modules.
they would exist under the namespace Uize.Test.Performance |
|
they would be combined into a test suite module as Uize.Test.PerformanceTests |
|
they would be runnable in the regular UIZE Unit Tests example | |
as real modules, they would automatically have reference documentation, which could contain the full description / explanation for what the tests aim to establish |
some tests could become examples | |
some tests could be eliminated |
18.3. For Unit Test Build Scripts
Consider testing the loading of modules in isolation of other modules, in order to determine that modules correctly declare their dependencies, without having missing declared dependencies masked by other modules that were loaded and built before them that shared some of their dependencies.
This approach would still not be foolproof, however, since merely loading and building a module can't determine that all modules that are needed are declared in the required
list, since some required modules may only be accessed during runtime execution of some code within a module.
18.4. Test All JavaScript Files
It would be nice if the build script could also test code besides and other JavaScript files throughout the folder hierarchy of a site, rather than just testing the modules.
There might be numerous issues with testing arbitrary JavaScript files found spotted throughout a Web site's folders. For one thing, such JavaScript files may not be written as Uize
JavaScript modules, and such files may not be able to be loaded without causing errors if they depend on other JavaScript code that they don't declare dependencies on.
19. Node as a Widget
a lot of widgets have to synchronize state to DOM nodes | |
to make it easier for widgets to optimize their performance and minimize the number of times that they touch the DOM in performing UI updates, it might be helpful to provide a representation of the state of a DOM node in the form of a widget. In this approach, DOM nodes are instead represented as node type child widgets. Instances of node type widgets can carry state for a node while the widget is not yet wired and while the node does not exist, to be synchronized with the node when the widget is wired up later. |
In what different ways do widgets touch DOM nodes?
modifying the className to reflect state | |
modifying position | |
modifying dimensions | |
modifying color | |
wiring event handlers |
20. Test Modules to Implement Next...
Uize.Test.Uize.Template |
|
Uize.Test.Uize |
|
Uize.Test.Uize.Json |
|
Uize.Test.Uize.Color |
|
Uize.Test.Uize.Color.Util |
|
Uize.Test.Uize.Color.xUtil |
|
Uize.Test.Uize.Curve |
|
Uize.Test.Uize.Build.Scruncher |
|
Uize.Test.Uize.Curve.Rubber |
|
Uize.Test.Uize.Curve.Mod |
21. Value Transformer Function
a function that is flagged as a value transformer | |
it is up to each function or method to determine if they will support value transformers or not |
21.1. - good candidates for methods to support value transformers...
myInstance.set | |
MyClass.set | |
Uize.Fade.Factory.fadeProperty | |
Uize.Fade.Factory.fadeProperties | |
Uize.Fx.fadeStyle | |
Uize.Dom.Basics.setStyle, myWidget.setNodeStyle | |
Uize.Dom.Basics.setProperty, myWidget.setNodeProperty |
makes sense where the value for which a value transformer is being specified is intended to set a new value for something for which a current value is known, where the new value is derived by applying the specified value transformer to the current value |
21.2. Semantics
// plus value transformer slider.set ({value:Uize.add.x (10)}); slider.set ({value:Uize.x ('x + 10')}); slider.set ({value:Uize.x (function (x) {return x + 10}})); // boolean toggle value transformer slider.set ({value:Uize.not}) slider.set ({value:Uize.x ('!x')}) slider.set ({value:Uize.x (function (x) {return !x})}) // multiply value transformer slider.set ({value:Uize.multiply.x (10)}); slider.set ({value:Uize.x ('x * 10')}); slider.set ({value:Uize.x (function (x) {return x * 10})}); Uize.multiply.x (10) Uize.x ('x * 10') function (x) {return x * 10} Uize.add.x (10) Uize.x ('x + 10') function (x) {return x + 10} Uize.subtract.x (10) Uize.x ('x - 10') function (x) {return x - 10} Uize.divide.x (10) Uize.x ('x / 10') function (x) {return x / 10} Uize.mod.x (10) Uize.x ('x % 10') function (x) {return x % 10} Uize.not Uize.x ('!x') function (x) {return !x} Uize.round Uize.x (Math.round) Uize.x ('Math.round (x)'); function (x) {return Math.round (x)}
21.3. Behavior
for any state property that is not defined in its property profile as allowing function type values, a function value specified as the new value for the property in a call to the set method will be treated as a value transformer on the current value of the property | |
for any state property that is defined in its property profile as allowing function type values, but that is not defined as allowing value transformer function values, a function value that is a value transformer function and that is specified as the new value for the property in a call to the set method will be treated as a value transformer on the current value of the property | |
for any state property that is defined in its property profile as allowing function type values and that is also defined as allowing value transformer function values, a function value that is a value transformer function and that is specified as the new value for the property in a call to the set method will simply be set as the new value for that property |
21.4. Possible Implementation
function _makeValueTransformer (_functon,_extraArguments) { var _valueTransformer = _extraArguments && _extraArguments.length ? function (x) {return _functon.apply (this,[x].concat (_extraArguments))} : function (x) {return _functon.call (this,x)} ; _valueTransformer.isValueTransformer = true; return _valueTransformer; }
var _cachedValueTransformers = {}; Uize.x = function (_unresolvedValueTransformer) { var _valueTransformer = _unresolvedValueTransformer; if (typeof _valueTransformer == 'function') { if (!_valueTransformer.isValueTransformer) _valueTransformer = _makeValueTransformer ( _valueTransformer, arguments.length > 1 && Array.prototype.slice.call (arguments,1) ) ; } else { _valueTransformer = _cachedValueTransformers [_unresolvedValueTransformer += '']; if (!_valueTransformer) { _valueTransformer = _cachedValueTransformers [_unresolvedValueTransformer] = Function ( 'x', 'return ' + _unresolvedValueTransformer ); _valueTransformer.isValueTransformer = true; } } return _valueTransformer; };
22. Profiling Support
22.1. - a means for getting reports of the time that certain operations take on a page
23. Performance Questions...
23.1. - is there any difference between array.push (element) and array [array.length] = element ?
there appears to be, with assign-to-end being faster, but it is not significant. In building an array of 1,000,000 elements, it can make a difference of up to 2 seconds. Difference is most pronounced in IE. At scales of 1000 elements, it makes a difference of just 1 or 2 ms. The assign-to-end approach adds to code size a tiny bit, requires maintaining a separate array length counter variable, and doesn't offer the convenience of pushing multiple elements in a single statement. Assign-to-end with accessing the length property is definitely not advisable, since that would eat into the performance benefit and more significantly add to code size. |
23.2. - when getting a reference to a node, does it hurt to fall back to document.getElementsByName?
doesn't appear to make that much difference in cases of many lookups. Optimizing it out for most lookups might help a little bit, but it's not that compelling. |
does setting a style property on a node always incur a cost, even if the value you're setting is the current value? If so, does accessing and testing before setting improve performance? | |
what's the performance difference between Math.round (myNumber) and (myNumber + .5) >> 0 ? |
23.3. - what's the performance difference between...
function doSomething (value) { ... } doSomething ('value1'); doSomething ('value2'); doSomething ('value3'); doSomething ('value4');
...and...
for (var value in {value1:1,value2:1,value3:1,value4:1}) { ... }
if (condition) doSomething ();
...and...
condition && doSomething ();
24. Idea for Handling Functions That May Be Asynchronous
24.1. - approach 1
function blah (param1,param2,param3,_returnResult) { var _returnResult = arguments.callee.returnResult || _returnResult; delete arguments.callee.returnResult; doAsyncStuff ( function () { _returnResult (result); } ); arguments.callee.isAsync = true; } function callAsAsync (_functionToCall,_args,_returnResult) { _functionToCall.returnResult = _returnResult; var result = _functionToCall.apply (_args), thisCallWasAsync = _functionToCall.isAsync ; delete _functionToCall.returnResult; delete _functionToCall.isAsync; thisCallWasAsync || setTimeout (0,function () {_returnResult (result)}); }
using it...
callAsAsync (blah,[1,2,3],handleReturn); function handleReturn (result) { }
24.2. - approach 2
function isValidUser (_user) { var _result = new Async; doAsyncUserValidation ({ user:_user, callback:function (_isValidUser) { _result.returnResult ? _result.returnResult (_isValidUser) : (_result = _isValidUser) ; } }); return _result; } function callAsAsync (_functionToCall,_args,_returnResult) { var _result = _functionToCall.apply (0,_args); _result instanceof Async ? (_result.returnResult = _returnResult) : _returnResult (_result) ; }
using it...
callAsAsync ( isValidUser, ['timipoo'], function (_isValid) { // do next stuff } );
25. - the most challenging and difficult problems to solve
generic I18N resource string dependency system | |
CSS dependency resolution system | |
generic tracking/logging solution | |
unit testing |
26. Generic Solution for Tracking
in most cases, should not require code changes | |
ability, from the outside of code, to track any event of any type of widget | |
whether or not to track an event is determined by a configurable rule | |
with tracked events, ability to log any aspects of state state that might be interesting | |
tracking is basically just logging by a different name | |
tracking and logging as good candidates for Aspect Oriented Programming |
27. Only Change Property Values Using the set Method
Make sure that all setting of properties is done through the set method (i.e. no more adhoc setting by assigning using the private name). Use a regular expression to search through the code.
27.1. How to Find Offending Cases
To find most cases...
SEARCH REGEXP
/this\._\w+\s*=[^=]/
...or write a build script that scans through JS files, determines private names of declared state properties, and then uses those names in generated regular expressions to find direct assignments.
28. - factor out code for validating a value, where the validator can be...
a simple value, to which the value being tested should be compared for equality | |
a function, whose return result should indicate whether or not the value being tested is valid | |
a regular expression, which should be tested on the value to determine if it is valid | |
null or undefined, indicating that no test should be performed | |
an optional parameter, indicating whether or not equality, strict equality, or dynamic (or some better name) mode should be used |
28.1. - could be used...
Uize (modify Uize.recordMatches) | |
Uize.Dom.Basics.find |
29. - should factor out code to set HTML for frame or iframe (best implementation so far in Simple Doc Tester)
29.1. - while at it, should factor out code for replacing contents of any window, and move into Uize.Dom.Basics.setInnerHtml? Or Uize.Dom.Basics.injectHtml? Or should there be a window utilities package?
29.1.1. Uize.Browser.Window.write
Uize.Browser.Window.write (window,content)
Uize.Browser.Window.write (window,content,contentType)
Uize.Browser.Window.launch
29.1.2. Uize.Browser.Window.center
Uize.Browser.Window.center (window)
Uize.Browser.Window.center ()
29.1.3. Uize.Browser.Window.resize
Uize.Browser.Window.resize (windowOBJ,width,height,positionX,positionY)
Uize.Browser.Window.resize (window,600,400)
Uize.Browser.Window.resize (window,'max','max')
Uize.Browser.Window.resize (window,null,'max')
30. Key Aspects of a Framework
Solving fundamental problems in useful, convenient, friendly, understandable ways
performance (load time, interaction speed, memory usage (non-leaky)) | |
i18n / L10n | |
interaction logging & tracking | |
multivariate testing | |
application robustness & state management | |
skinning / theming | |
troubleshooting | |
sensible code reuse | |
ease of development | |
easy wow (effects and animation) | |
user help | |
interoperability |
31. - Uize.Delayed
31.1. Portion of Implementation
instanceMethods:{ cancel:function () { var m = this; if (m._timeout) { clearTimeout (m._timeout); m._timeout = _null; } }, perform:function (_actionFunction) { // support optional delay param var m = this, _delay = m._delay ; m.cancel (); _delay ? (m._timeout = setTimeout (_actionFunction,_delay)) : _actionFunction (); } }
31.2. Another Possible Implementation
Uize.caller = function (_context,_method,_params,_delay) { function _callMethod () { var _function = typeof _method == 'string' ? _context [_method] : _method; _function.callerThis = this; _function.callerArguments = arguments; var _result = _params ? _function.apply (_context,_params) : _function.call (_context); delete _function.callerThis; delete _function.callerArguments; return _result; } _callMethod.cancel = function () {}; if (_delay) { var _timeout, _caller = function () { var m = this, _arguments = arguments ; return (_timeout = setTimeout (function () {_callMethod.apply (m,_arguments)},_delay)); } ; (_caller.cancel = function () {if (_timeout) _timeout = clearTimeout (_timeout)}) (); return _caller; } else { return _callMethod; } };
31.3. Sample Usage
31.3.1. Approach 1
m._delayedAction = Uize.Delayed ({delay:1000}); m.wireNode ( node, { mouseover:function () {m._delayed.perform (function () {m.doSomething ()})}, mouseout:function () {m._delayed.cancel ()} } );
31.3.2. Approach 2
var _delayedCaller = Uize.caller (m,'doSomething',null,1000); m.wireNode ( node, { mouseover:_delayedCaller, mouseout:function () {_delayedCaller.cancel ()} } );
31.4. Thoughts
function () {m.setNodeStyle ('blah',{color:'#000'})} Uize.caller (m,'setNodeStyle,['blah',{color:'#000'}]) Uize.defer
What's good about closures for node event handlers is that they can access...
the node as this | |
the event as the first parameter |
What's bad about closures...
don't offer the delayed execution and cancelability supported with Uize.caller | |
code hangs on to state of closure's scope |
32. More Unobtrusive JavaScript Features
Implement more unobtrusive features, where no JS is needed inside markup, such as...
links that are flagged to pop open windows (perhaps with target="[some window name]") |
32.1. - expand/collapse
a lightweight way to get expand/collapse behavior into documents, without having to instantiate widgets |
33. Code Development (in order of priority)
33.1. **** property changed events refactoring
some events that were being fired in the onChange handlers were not being fired right at the end, but now they in effect are because of the way the Changed.[propertyName] event mechanism is implemented. Could this cause any weird order of execution problems? | |
the code registered in handlers for the Changed.[propertyName] event is now being executed as a batch for the changed properties after the whole batch of onChange handlers is executed. Could this cause order of execution problems, with some code already expecting the previous behavior? | |
the Uize.Widget.Options class is using the '' event to bubble up events from the button instances. The Uize.Widget.Button class was previously firing an event for change of the 'selected' property value. Because of the way that the Changed.[propertyName] event is currently implemented, this event will no longer be bubbled up. Could this issue with the '' event become a general problem? | |
perhaps onChange handlers should get the previous value for the property as a parameter |
33.2. - high-minded ideas
drag a value from any widget to any other widget that supports or has a child that supports the value as part of its interface | |
an easy way to bind two widgets to each other, so that values are syncronized | |
an orthogonal effect system that does not require widget code to implement effects | |
a codified system for registering optimized handlers for batch property changes (e.g. when value and value set are changed in one action) |
33.3. - memory usage optimizations
use prototype for state properties? |
33.4. - performance optimizations
a way to avoid calling onChange handlers at construction time |
33.5. widget ID scheme refactoring
33.5.1. other...
possibly come up with a different term for the root node of a widget (since we still have shell references in identifiers in various places, possibly just "root" instead of "shell") |
33.6. image border fade overlay
a convenient way to automatically overlay a border fade on images in a document
Uize.TextOutline: a little class to make it easy to create an outline effect around text, so that it can be legible when against a background of a similar tone (i.e. insufficient contrast)
33.7. Graphing DHTML
overlay for stock price chart images to highlight date and price at date
33.8. Image Letters Widget (Uize.Widget.Spritext)
an easy way to display words using character set table images
33.9. Auto-wired hide-reveal mechanism
for documents with sections of extra in-depth information that most users may not be interested. Allows the information to be available and read in the context of the larger document with an extra click, but without creating an overly heavy read for most users
33.10. Stars Rating Widget
a convenient way to have a rating value translated into a graphic of stars (whole and fractional). Could this be accomplished with a generalized bar / slider widget that has stars as its fullness bg?
Suggest / Auto-complete Widget
33.11. Utility routine to easily make a bunch of links open in a new window
mode to automatically detect links to larger images and wire to open correctly sized. Frill where linked thumbnail "throbs" outwardly to suggest enlarging. | |
mode to overlay alt/title on image with transparency effect |
33.12. Auto-viewer for Images
Simply put in image tags in your page, let the auto-viewer present the images to the user in a professional layout
covenient skins for easy setup |
33.13. IDEAS
facilitate multiple controls controlling the same data (e.g. two buttons for the same action) -- might be two different object instances linked in some way | |
combined with conformer mechanism, perhaps also the concept of discrete set value and resolved (i.e. calculated value) value -- so if there was a valid range and there was an attempt to set outside the valid range, the common value used by most code would be the resolved value, but the set value would also still be stored and accessible through some interface |
33.14. Puzzle Game
takes an image and splits it up into puzzle fragments and then lets the user drag and drop them in place | |
race against time | |
mode where puzzle split if they haven't been used in a while | |
mode where unused pieces fade out over time | |
difficulty level can affect number of pieces | |
difficulty level can increase piece dimension variation |
expand/collapse module for documents (to attach logic in similar way to Uize.Widget.Tree.List) | |
letters remaining widget implemented using slider indicator widget | |
XY selector widget | |
nifty javascript bookmarklets that allow you to view the online version of a local file, or the local version of an online file | |
table color fader | |
text color fader | |
ordering widget (for customizing table column order, etc.) |
34. BUGS
34.1. Safari
for all behaviors that are modified using the Ctrl key, should also be modified by the metaKey (for Mac users) |
34.1.1. ...ImagePort.Draggable
no way to initiate Ctrl modifier key behaviors
34.1.2. ThumbZoom
zoom out effect is very choppy | |
fade-to-black effect is very choppy | |
weird flash sometimes at start of fade-to-black | |
positioning is not adjusted for page being scrolled | |
zoomed image is spontaneously dismissed if page had scrollbars (some weird event thing?) |
34.2. IE (in order of priority)
"Uize.WiseLoad Example": mouseover opacity effect on thumbnails not working (and trying to enable it using filter:alpha(opacity=...) was completely hosing my machine) |