PERGOLA

DOCUMENTATION
  1. Introduction
  2. Configuration
  3. The Call to the Constructors
  4. The Classes
  5. The Node Builder (SVG)
  6. The Node Builder (HTML)
  7. User Define Events and Functions
  8. Object Extensions
  9. Skins
  10. Making a New Skin
  11. Symbols Library
  12. Shapes Library
  13. Filters Library
  14. Cursors Library
  15. Known Issues

INTRODUCTION

DESCRIPTION

Pergola is a JavaScript framework equipped with a powerful and innovative interface for accessing the SVG implementations library, and a rich library of SVG controls and interface objects. It is designed to provide developers with a powerful tool for building web applications, User Interfaces, presentations and more.

Ease of use has been a constant concern from the very beginning of the conception and we hope you will find Pergola as easy to use as we planned it to be.

The architectural scheme of Pergola is based on class inheritance and prototype extensions of the subclasses, and it's built around a placeholder object, establishing a namespace, a superclass and subclasses.

The subclasses are designed to produce micro and macro assemblies constituting interface elements. There are no classes defined for SVG elements. Instead, Pergola uses a revolutionary node builder which, as we will see, allows to build any SVG element in the most simple and intuitive manner, without any restrictions or constraints, using SVG grammar. This concept comes from a candid idea, that of using directly what an SVG implementation really is, a library, rather than going through the waste process of building a pseudo-library with a pseudo-language. Occasionally, whenever that proves necessary, Pergola uses pseudo-attribute names.

Pergola also implements a very powerful feature to some of its classes which allows the user to define, very simply in the call to the constructors, any SVG attributes as instance properties, using SVG grammar. This mechanism also has the advantage of producing SVG elements which are clear of all those attributes that have initial values, as per the specification, resulting in an optimized SVG file as it would be if written manually by a competent SVG developer.

This elegant and modern concept could be applied thanks to the fact that there were no backward compatibility constraints and the library, targeting exclusively SVG, could then be designed from scratch.

Pergola implements the inherent logic for all of its SVG interface controls and objects, and, partially, the underlying system logic.

We believe that the learning curve for Pergola will be shortened by the fact that the developer will “think” SVG during development.

Some other features are:

Pergola is fully compatible with the major SVG implementations (ASV, Firefox, Opera, Safari, Chrome).

Throughout this documentation we will reference the rich demo examples provided with the package, most code examples coming directly from them.

USE

An interface object is created by declaring an instance of a particular class. The types of classes range from those which produce concrete objects to those which accomplish particular tasks. The concrete objects are always compound, the most complex being assemblies of instances from several other classes.

Some classes, where appropriate, are provided with a level of systemic intelligence and environment awareness designed to anticipate the needs of the developer who wishes to go beyond the assembly stage. Particular care has been taken with the implementation of User Define Events and Functions in order to give the possibility of building truly intelligent applications. In this respect, please note that you dispose of two possible scenarios:

  1. You use window instances. Pergola will act like a system, and if you place objects as standalone, those objects will not be subject to the hierarchy of system components and it could prove difficult to manage their visibility in all situations. In this scenario you would place all of your objects in appropriate container objects, windows or panels.
  2. You place your objects around the way you need, following a particular layout. In this type of scenario you just need to remember that panels are preemptive and situated in an upper layer.

Pergola does not have classes for creating SVG elements. As we have seen in the Introduction, those are created in a very simple and intuitive manner by calling directly the node builder method, passing one object as parameter where any SVG attribute/value pair can be declared using SVG grammar.

TYPOGRAPHIC CONVENTIONS

The following formatting conventions are used in this documentation:

Bold
Property which is the subject of analysis.

Italic
All property names.

Constant width
JavaScript code.

Constant width italic
Arguments, property values and placeholders that should be replaced with actual values in your program.

Constant width
Code referencing the examples in the package.

“name”
Names

CONFIGURATION

The configuration of a project is a very simple procedure. Each of your projects using Pergola has a configuration file where a few environment variables are set. A config template can be found in the root folder of the package. Each example demo is treated as a different project and each has its own config file at the root level in the demo's folder. The config files which accompany each demo are identical.

The pergola.settings property is an object with the following properties:

skin : this property designates the skin to be used by the project. Pergola has one predefined skin, its name is rubber and it is a property of the pergola.skins object, defined in the skins file. You should not alter the rubber skin. In the SKINS and MAKING A NEW SKIN chapters the principles and the procedure for creating a new skin are explained in detail.

theme : the base color for the current skin. You can use a color keyword name, a custom color name or specify a color in any legal format.

pergola.path : this is relative to the arborescence of your project. For each of the demos this is set to "../../pergola 1.0/".

pergola.debug : It specifies if the built-in mini debugger has to be used. This property can get a Boolean or a Number (number of lines). Default number of lines is 20. It should be set to false before releasing your project.

THE CALL TO THE CONSTRUCTORS

This chapter gives an overview of how to create instances. For the generic example and the discussion on properties in this section we are going to use a loose language. The CLASSES chapter is more technical and the properties will be reviewed there using a stricter language.

For the calls to the constructors Pergola uses a technique designed to take full advantage of OOP, not simply an OOP writing technique.

Consider the call consisting of two phases. The first phase is the call to the class constructor where your object is initialized and inherits the prototype properties of its class and the class's superclass. It also gets a name property and an XML naming conventions conformant id property derived from the name property:

var myButton = new pergola.Button("My Button");

The unique parameter is of type String. This will be the name of your object that can be used for things like titles for example, and can be extremely useful for debugging. You are free to use any characters, including spaces. To build the object's id all non conformant characters will be replaced by the underscore ("_") character. The parameter is optional, but we think it is the best habit to acquire as it will be of great help while inspecting the DOM tree to trace your bugs. If no parameter is passed the object will receive a generated, unique XML name and the id will be equal to the name.

The second step is the call to the class's build() method:

myButton.build({
  properties
});

The unique parameter is of type Object. Here is where we define instance properties and override prototype properties for the object that is being constructed, a button in our example. As we will see, some classes have special prototype properties with initial value of null with the sole scope of being overridden to become then instance properties. This seemingly puzzling notion unveils a powerful mechanism which allows us to pass any SVG attribute name as property name, along with other consequent advantages. This mechanism is implemented in some of the classes only. It is important to understand that declaring such properties in the calls to the constructors' build() methods for classes that are not designed to handle them, makes those be considered simply as instance properties, with no processing.

The interest with this split call technique is that the object is already initialized after the first call to the class constructor, in other words it is self-aware, meaning that when we call the build() method the object can refer to itself, and its prototype properties can be used in expressions. This also has a notable consequence: no need for a $super type of idea. Imagine that the class or its superclass has a prototype property named say with value "Hey"; then suppose your new instance needs to say "Hey Joe". No $panic:

myButton.build({
  ...,
  say: myButton.say + " Joe",
  ...
});

but also, before the call to the build() method:

myButton.say += " Joe";

The line above shows that this technique gives the possibility to set new properties –instance methods for example– in between, i.e. after the call to the class constructor and before the call to the build() method.

Last but not least, this technique also gives the terrific opportunity to just define a property object in same place, while getting it actually built at a later stage. A simple and typical example is a list separator:

separator: new pergola.Separator()

This line is found in the call to the build() method of the Menu class (see the menus example). At this level the internal geometry of the menu is not known, neither if any prototype properties are, or will be, overridden. The separator is declared as a property of a particular menu item, typically the last one of a group of items.

Likewise, in the same example file, you will notice that:

menu1: new pergola.Menu("Menu 1"),
menu2: new pergola.Menu("Menu 2"),

menu1 and menu2 are declared as properties of the menu object, while the calls to the build() method appear further down. They could even be in some other file or get built dynamically, with all that this implies.

This technique results in a very clear, flexible and easy to manipulate structure of the calls, not forgetting the opportunity to carry out all sorts of operations in between, and provides a good level of interactivity and intercommunication between objects which can prove very helpful when building applications that need to go beyond the simple playing around with objects. Pergola is designed to give you that power.

PROPERTIES

For the generic example and the discussion on properties in this section we are going to use a loose language. The CLASSES chapter is more technical and the properties will be reviewed there using a stricter language.

Each of those classes that are designed to produce concrete objects has a set of prototype properties describing the default rendering of an instance. It is important to know that Pergola uses the SVG grammar for naming these properties. Those property names are then very simply SVG attribute names. Pergola only uses pseudo-attribute names whenever a property name is already taken, as is sometimes the case with compound objects. To clarify this concept let's take an example:

An instance of the Button class, or to put it simply, a button, is a compound object made of two copies of the same primitive shape, the mask and the button. These two instance properties are assigned in the build() method and are references to their respective primitive elements in the DOM (referenced calls to the node builder).

mask and button have a different fill, of course, and the fill property of the prototype can only be applied to one of them, the button in our case. For the mask fill Pergola uses a property with pseudo-attribute name maskFill.

Note that for those SVG attributes which contain a dash "-" (really a minus sign), or any other character that is illegal for variable/property JavaScript names, we use the string syntax:

myButton.stroke
myButton["fill-opacity"]

All the relevant elements constituting the concrete object are referenced, allowing the user to programmatically manipulate them without the need to have recourse to the getElementById() method. Example:

myButton.group.setAttributeNS(...);

Bound to this documentation are property sheets enumerating the properties for each class. The properties are categorized. The categories are:

We encourage strongly to take a close look at those quick reference sheets and keep them handy as this can prove to be very advantageous. Each listed property contains information about its type, allowed values, initial value, whether it is required and more.

Back to our button example. Now we can take a look at the object that we pass as parameter to the build() method. Reduced to its simplest expression the call can be:

var myButton = new pergola.Button();
myButton.build();

where we can see that the object parameter can be skipped altogether. The button will have its default appearance and behavior and will be appended to pergola.user at position x:0 y:0.

Not terribly exciting. Let's consider a more interesting case, the first button in the demo:

var basicButton = new pergola.Button("Basic Button");
basicButton.build({
  parent: g,
  quickTip: "this is the basic, default button",
  ev: "mouseup",
  target: legend.tip1,
  fn: legend.hilight
});

and let's review the properties one by one:

parent
The parent node to which the button group will be appended. This implies that the node already exists in the DOM tree. As we will see under the CLASSES chapter this is an inherited property with the initial value of pergola.user. If this property is not overridden in the call the object will be appended to the element <group id="pergola_userLayer"> without notification. There is one exception to this, the Panel class overrides the parent property to pergola.dialogs, reference to the element <group id="pergola_dialogs">, which is in <group id="pergola_syatemComponents">, the topmost layer. In our buttons demo we have first created a group to be the parent node of all the buttons.

quickTip
This property can get three different formats, a string, an array or a reference to an existing quick tip object. Full explanation under the QuickTip header in the CLASSES chapter.

ev
One or more user events which will be registered independently of those registered by default. Remember that as per the JavaScript specification the order in which events of the same type are handled is not guaranteed and is implementation dependent, meaning that if you register a mouseover, it may be processed before or after the one registered by default.

target
The object that is the target on which the user function will act upon. This property will always be referenced in your functions as myButton.target or more probably this.target and must not be confused with the target property of the event object, the evt.target.

fn
The handler function associated with the user events.

For the full explanation about these last three properties and the several types allowed for the functions refer to the User Define Events and Functions chapter. For now we're happy to just know that some of the demo buttons have a “mouseup” user event, one or more lines of the legend as target, and an associated function.

Let's now take a look at the second example, where we can see some more properties:

var button = new pergola.Button();
button.build({
  ...,
  extra: {
    rx: 3,
    transform: "rotate(-45)"
  },
  symbol: {
    symbol: pergola.symbols.arrow.large.left,
    x: 5,
    y: 5
  },
  ...
});

extra
Aha! An object where we can define ANY property named after SVG attributes and using, as appropriate, SVG syntax! (*) The properties you are assigning here in the extra bject are not processed, they are simply integrated with the parameter object and passed over to the Node Builder (actually, some properties are processed for fine positioning of objects in order to achieve correct rendering). This means that it is your responsibility to see that the attributes are coherent with the element and well formatted. It is important to remember that any property designating an SVG attribute that is not part of the class's default set, declared in the prototype, will be ignored unless it is a property of the extra object. Think of the extra object as containing any extra SVG attributes that are not inherited properties of the instance.

Note that not all classes are enabled to process the property extra. In this version of Pergola the feature is implemented in these classes: Button; CommandButton; DialogButton; Frame; Progress; TopBar. Nevertheless, we will see that this technique is widely used. For text, for example, which is not a class, we define a text instance property and we assign an object where we declare properties named after attribute names of the <text> element. All those objects that are likely to have any text or captions are configured to process the text property. This is also true for several other properties of type object, where we can define transformations, coordinates, paint attributes, etc. always using strictly SVG grammar/syntax. To enumerate all these possibilities would be tedious, experience through use being a better teacher in this case. The numerous examples clarify this concept.

The extra property is destroyed after construction of the object and cannot be queried.

(*) To be precise about the “SVG syntax” statement above, when assigning the property in the call, the operator is “:” and not “=”, of course. Also, while numeric values can be of type Number, numeric values with unit identifiers are of type String, as in: 'font-size': '9pt'

symbol
This property gets an object with a limited number of properties. Full explanation in the SYMBOLS chapter.

Looking further in the buttons examples we find:

var button4 = new pergola.Button("Button 4");
button4.build({
  ...,
  hasTextEffect: false,
  text: {
    x: 44,
    y: 36,
    textNode: "TALK",
    fill: "none",
    stroke: "#FFFFFF",
    'stroke-width': 1.5,
    'font-family': "'Arial Black'",
    'font-size': 22,
    'font-weight': "bold",
    transform: "scale(1 .5)",
    'text-anchor': "middle",
    'letter-spacing': 2,
    kerning: 0,
    'pointer-events': "none"
  },
  ...
});

hasTextEffect
Determines whether the text has the video inverse animation effect.

text
This property is an object where you can set any SVG text attribute using SVG syntax. Its property textNode is a pseudo-attribute name and gets a string; it is processed by the node builder to automatically create and append the text node to the text element.

Note: the text property mutates, its value changes to become a reference to the object's <text> element, which can be manipulated. For example a button could say “TALK” in its first state and say “SILENCE” once clicked:

myButton.text.firstChild.data = "SILENCE";

but also by calling the prototype method textDataUpdate(), a method of the superclass prototype:

myButton.textDataUpdate(new value);

One other interesting property in the monaLisa button example:

var monaLisa = new pergola.Button("Mona Lisa");
monaLisa.build({
  ...,
  image: {
    "xlink:href": pergola.path + "rsc/symbols/MonaLisa.jpg",
    width: 53,
    height: 40,
    x: 3,
    y: 3
  },
  ...
});

image
This property gets an object and is similar in scope to the symbol. They can be used together as in the monaLisa stamp example where the image is affected to the mask and the symbol to the button. Notice that this is independent of their fill value. The image object can only have those properties shown.

As we have seen already, not all classes are designed to process the special properties that we have seen in these examples.

WHERE DO I FIND THE DEFAULT SET OF PROPERTIES?

Legitimate question. Those properties are in the prototype extension of each class. But... that is not where they actually get their values! Instead they are simply references to properties set in the skin. This is so that you can edit them, if you wish, in all tranquility, no searching, no headache, no stress, no fear for non-hackers.

Before anything we recommend that you first read carefully the first section of the SKINS chapter. The skin is a function that does two things: it creates the gradients and patterns and creates the pergola.presentationAttributes object. This object fills a role similar to that of CSS. Each of its properties is an object which refers to a particular class by a close name association, for example the button property of this object correlates with the Button class, as simple as that.

The properties of these objects are mainly visual and geometrical attributes, but not only. A typical example to check out is the slider object: it has a showValue property set to false. If you want all of your sliders to show by default their value while operating, you will then set this property to true (since we are talking sliders, note that scrollbar sliders are a different class from standalone sliders).

Adding a property will not have any effect. Removing a property will most certainly result in a runtime error. Unless you particularly hate yourself some day, you shouldn't try anyway.

The best way to proceed is of course the one suggested in the SKINS and MAKING A NEW SKIN chapters. You would make a copy of the default skin (“rubber”), give it some cool name and then edit your new skin. This shows a huge advantage over a classic CSS file, you can have two or more sets of definitions in the same file! Your different projects can then point to the same skin file and for each of them you will then set the name of the desired skin in the config file that accompanies it.

One other notable and invaluable benefit in using this orthodox method is that this way you won't destroy the precious contribution that senior designer Jayne De Sesa gave to the Pergola project.

Other prototype properties of the classes are used for internal logic and are of no interest to us.

THE CLASSES

As we have seen in the Introduction, the architectural scheme of Pergola is composed of a placeholder object, named pergola, a superclass and subclasses, all in the pergola namespace.

The pergola object establishes a namespace and its properties are basically environment variables and some construction methods that accomplish tasks like building the DOM tree structure, building system symbols, shapes, filters, cursors, quick tips etc., as well as some system elements. It also defines a group that will be the default parent node of all user SVG objects, identified as pergola.user.

The superclass, a property of pergola named Class, is the class from which most subclasses inherit. All the prototype methods are defined in pergola.Class. This superclass also defines some class methods.

The subclasses, all properties of pergola, are specialized constructors and each has its own prototype extensions besides the inherited prototype.

We can subdivide the classes in two categories, concrete and abstract (in the literary sense, not C++), the majority belonging to the former and designed to produce SVG visible or invisible objects. They are always subclassed to pergola.Class. Other classes designed to carry out particular tasks, like Timer for example, are not necessarily subclassed.

The superclass defines some very common rendering properties which are inherited by the subclasses. These properties are reviewed here and will not be reviewed under the individual classes headers. These are:

The x and y properties
Number. With initial value of 0, these ALWAYS refer to the position of the object, never to the coordinate attributes of any particular primitive that is a constituent of the object, independently of the element's shape –fine positioning of any <rect> elements within their container is achieved by taking into account their stroke width. In other words x and y represent the position of the object's container, whether <group> or <svg>, in respect to the document's viewport or the eventual parent node to which the object was appended. For objects that can be dragged like windows, panels and sliders, the release() method of the pergola.Dragarea class updates these properties. In cases where the update of these properties is not sought, the updateCoordinates property of the Dragarea class, initially true, is overridden in the initialization object that is passed to the Dragarea activate() method. Example of Dragarea activation further down in this chapter under the Dragarea section.

The 'font-family' property
String. Assigned in pergola.presentationAttributes.all in the skin, this property is not only inherited but also applied to the document.

The 'font-size' property
Number or String. Assigned in pergola.presentationAttributes.all in the skin, this property is not only inherited but also applied to the document.

Note that, as we have already seen, the two properties above use the string syntax for property names allowing the use of SVG attribute names in their exact format.

The stroke property
Number or String. Assigned in pergola.presentationAttributes.all in the skin.

The superclass prototype also defines a parent property designating the default parent node for all objects (except for some categories of system objects):

The parent property
Reference. The value of this property is: pergola.user, which references the group with ID “pergola_userLayer” in the DOM tree.

Some classes like Panel (dialogs) and Quicktip override the parent property to make sure dialogs and other system objects always remain visible.

When building a top level object or container it is wise to not override the parent property, but for their descendant objects, which most likely are meant to be appended to a container of the owner object, the parent node must be specified, or else your objects will be scattered all over. You will not get any notification that your object has been appended to its default parent node.

Note also that Pergola builds some system layers (groups) properly stacked, resulting in this DOM tree:

element id referenced by
<g> "pergola_desktop" pergola.desktop
<g> "pergola_background" pergola.background
<g> "pergola_userLayer" pergola.user
<g> "pergola_systemComponents" pergola.systemComponents
<g> "pergola_dialogs" pergola.dialogs
<g> "pergola_colorpicker_dialog" pergola.colorpickerDialog
<g> "pergola_notification" pergola.notification
<g> "pergola_quickTips" pergola.quicktips
<g> "pergola_shadowSelector" pergola.shadowSelector
<rect> "pergola_dragarea" pergola.dragarea
<g> "pergola_taskbar" pergola.taskbar

Your top level objects should always be children of pergola.user, ensuring that dialogs and quick tips, as well as other critical system objects, never get overlaid. If the parent property of a top level object can be safely skipped, it should never be skipped for its descendants.

Before reviewing the classes individually we are going to examine here some instance properties that are very recurrent, as we can see from the examples. These are:

The owner property
It designates an owner object. It is a reference, a pointer to an existing object. For many classes this property is required –implying that there is no such prototype property– and has a twofold usage, that of providing access to the owner object's geometrical and other relevant properties during the construction phase of an instance, and that of establishing an upstream type of hierarchical communication between objects. This property can prove useful for example when establishing interactivity dynamically; we may not know, programmatically speaking, the owner object of some object, but the object does:

myObject.owner.name
myObject.owner.x
myObject.owner.constructor

In an anonymous context we would first need to test for the existence of the owner property of course:

if (myObject.owner) ...;

The idea is that of providing a facility similar to the DOM parentNode property. Note that the owner property always designates an object, not a class or superclass. To reach information about those we dispose of the following read only properties:

class.prototype.constructor :the class itself
class.baseConstructor :the superclass
class.superClass :the superclass prototype

We can say then that Pergola produces a model which tries to be conformant with the document's model, the DOM. Nevertheless, the DOM tree is not referenced integrally. Only those nodes that are considered relevant for object manipulation are referenced by instance properties as direct pointers. All other nodes that we may wish to access are always accessible through DOM and SVG DOM methods and properties.

The ev, target and fn properties
These properties relate to the User Define Events and Functions and are covered exhaustively under the USER DEFINE EVENTS AND FUNCTIONS chapter.

Let's review now the classes in alphabetical order. Under each class we'll first see a list of those prototype properties that can be overridden, with their initial values. Prototype properties which are not of any interest for the user are not listed. The full list of properties for each class can be found in the properties sheet bound to this documentation. Following that is the list of properties (in bold italic) which reference the significant elements and component objects that constitute the object. Note that these properties may have names that correspond to the SVG element to which they refer, but this is not a rule. Then follows a description of the class with one or more code examples, mostly calls to the constructors' build() methods. The instance properties found in the object parameter of those calls are examined case by case; the type(s) of their value is first shown, followed by a description.

All the classes are properties of the pergola object and are subclasses of the pergola.Class superclass. Some classes are subclassed to another class and when this is the case it is specified in the description.

Background; Button; CheckBox; ChildDoc; ColorBoxSelector; ColorPicker; ComboBox; CommandButton; DialogButton; Dragarea; Frame; Layout; ListItem; Menu; Menubar; MenuItem; MenuList; Panel; PopupList; PopupListItem; Preview; Progress; QuickTip; RadioButton; Scrollbar; ScrollSlider; Selector; Separator; ShadowSelector; Slider; Tab; TabsDock; Taskbar; ToolBar; ToolButton; TopBar; ValueInputBox; Window; WindowTab; WindowTopBar.

The Load and Timer classes are not subclassed.

Note: for pergola.presentationAttributes we will use the shortcut skin throughout the chapter.

Background class
PROTOTYPE PROPERTIES

width:pergola.width()
height:pergola.height()
fill:skin.background.fill
stroke:"none"
opacity:skin.background.opacity
'xlink:href':""
display:"block"
pointerEvents:"visiblePainted"
preserveAspectRatio:"xMidYMid slice"

The properties opacity and 'xlink:href' apply to the background image, if any.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
rect<rect> element
image<image> element

The Pergola desktop and all windows build an instance of this class. Practically any object can have a background instance. Example:

pergola.background = new pergola.Background("pergola_background");
pergola.background.build();

A window's background as defined in the Window build() method:

this.background = new pergola.Background(this.id + "_background");
this.background.build({
  parent: this.control,
  x: this.margin + .5,
  y: this.paneY + .5,
  width: 0,
  height: 0,
  fill: this.fill,
  stroke: this.stroke
});

We notice the width and height properties set to 0. They apply to the rect and image properties and get automatically updated for objects that can be resized, like the windows are. For your objects other than those you will have to set these two properties to their appropriate values.

Button class
PROTOTYPE PROPERTIES

fill:skin.button.fill
'fill-opacity':0
stroke:skin.button.stroke
maskFill:skin.button.maskFill
large:skin.button.large
small:skin.button.small
hasVisualEffects:true
hasTextEffect:true
extra:{}
shape:null
text:null
symbol:null
image:null
quickTip:null

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
buttonGroup<g> element
mask<rect> element
button<rect> element

As we already saw in the chapter THE CALL TO THE CONSTRUCTORS, under the Prototype properties and instance properties header, those properties which designate attributes in the SVG namespace apply to the button while the mask attributes are pseudo-attributes: fill versus maskFill.

Note: fill and maskFill are and remain properties of the object. Don't be tempted to query those properties like this: myButton.mask.maskFill !

The button3 and button4 examples:

var button3 = new pergola.Button("button 3");
button3.build({
  ...,
  size: "small",
  ...
});

var button4 = new pergola.Button("button 4");
button4.build({
  ...,
  width: 88,
  height: 28,
  ...
});

The width, height and size properties
Number or String. width, height and size are not prototype properties, they are instance properties. If none of these properties are set in the call then the button will have its width and height properties set to the default large size of 20 px 20 px (as defined in the skin). You can assign those properties explicitly or assign the size property instead, which is a shortcut. Its two possible string values are "large" and "small". Since "large" is implicit, in practice you would only use the "small" value.

The monaLisa button example:

var monaLisa = new pergola.Button("Mona Lisa");
monaLisa.build({
  ...,
  extra: {
    'stroke-width': 4,
    'stroke-dasharray': "3,3",
    'stroke-dashoffset': 0
  },
  ...
});

The extra property
Object. As we've seen in the Introduction, Pergola implements a powerful feature to some of its classes which allows the user to define any SVG attributes as properties of the extra object, in the call to the build() method, using SVG syntax. The property/value pairs of this object are not processed in any way other than being incorporated. This means that it is your responsibility to see that these attributes are coherent with the element and well formatted.

Another example is the second button with the transform property:

var button = new pergola.Button();
button.build({
  ...,
  extra: {
    rx: 3,
    transform: "rotate(-45)"
  },
  ...
});

We must remember that those attributes that are prototype properties do not go in the extra object. But if we accidentally did that, they will simply override the prototype homonym properties in some, but not all, cases. On the other end, if we declare an attribute which is not a prototype property elsewhere than in the extra object, then it will be simply ignored. So when we pass a set of super-cool SVG attributes and we don't see it happening it probably means they're not in the right spot.

A simple case: the Button class does not have a prototype property rx. Suppose we want our button with rounded corners. If rx is not a property of the extra object then our button will have straight corners. This is so because the construction of the object consists of several phases during which a transient object representing the SVG element gets progressively built and then passed to the node builder. The first phase only considers the prototype properties. In the phase where the extra object gets treated, its properties become properties of the transient object with no further processing (which implies that conformity errors in attributes or their values get through to the node builder).

The button5 example:

var button5 = new pergola.Button();
button5.build({
  ...,
  symbol: {
    symbol: pergola.symbols.arrow.large.down,
    x: "center",
    y: "center",
    scale: ".65 1.5",
    fill: "#80C8FF"
  },
  ...
});

The symbol property
Object. This object and its properties are examined in detail in the SYMBOLS chapter.

The monaLisa button example:

var monaLisa = new pergola.Button("Mona Lisa");
monaLisa.build({
  ...,
  image: {
    "xlink:href": pergola.path + "rsc/symbols/MonaLisa.jpg",
    width: 53,
    height: 40,
    x: 3,
    y: 3
  },
  ...
});

The image property
Object. This property is an object with the set of required attributes for the <image> element, i.e. 'xlink:href', width and height and optionally x and y. Nothing more. Other attributes necessary to the proper functioning of the button will be set automatically. If you think you need to pass other <image> element attributes you can, as long as you make sure you skip opacity and 'pointer-events'. If set, the image is appended after the symbol, independently if it was declared before, meaning that you will always see the symbol when the button is inert and the image when rolling over it. In the monaLisa button the symbol is the negative picture and the image is the positive one.

The button4 example:

var button4 = new pergola.Button("Button 4");
button4.build({
  ...,
  text: {
    x: 44,
    y: 36,
    textNode: "TALK",
    fill: "none",
    stroke: "#FFFFFF",
    'stroke-width': 1.5,
    'font-family': "'Arial Black'",
    'font-size': 22,
    'font-weight': "bold",
    transform: "scale(1 .5)",
    'text-anchor': "middle",
    'letter-spacing': 2,
    kerning: 0,
    'pointer-events': "none"
  },
  ...
});

The text property
Object. An object where you can set any SVG text attribute using SVG syntax. The textNode property is a pseudo-attribute name and gets a string. A text node will be created automatically.

Note: the text property mutates. It ceases to be the original object to become a reference to the object's <text> element.

The amoebaButton example:

shape: {
  element: "path",
  geometry: {
    d: "M16.14,2.53C23.64 12.1 29 9 33.3 14.23C37.1 18.85 
       29.34 22.78 35.7 28.6C39.79 32.35 36.08 39.69 28.12 
       40.31C-3.59 42.78 8.98 27.12 5.5 21.71C-5.96 3.87 
       11.69 -3.14 16.14 2.52z"
  },
  ...
});

The shape property
Object. Buttons have another nice feature, you can assign a shape from the shape library or even define your own shape on the fly, and that can be any SVG primitive. In practice there is no difference in the definition of the shape, the user defined shape has the same format of a library shape. The difference lies in the fact that the latter is a reference to one of the shape objects of the library, while the former is the object itself. In all cases a shape object has exactly 2 properties, element and geometry.

Question: why is the property geometry an object? Very simply because the element could have more than 1 geometrical attributes, an ellipse for example, although shapes will most likely be paths, polylines and polygons. The name of this property is also in itself a reminder that painting and other attributes do not go here.

The button4 example:

var button4 = new pergola.Button("Button 4");
button4.build({
  ...,
  quickTip: {
    "you can place text and symbols together",
    "in a button. I don't have a symbol, but",
    "just to let you know..."
  },
  ...
});

The quickTip property
This property can be of type String, Array or Object. In the latter case it will be a reference to an existing pergola.quicktips object as those found in the qtips.es file.

Note: the quickTip property mutates. It ceases to be the original string or array that it was, to become a reference to the actual quick tip object of the instance. A prototype method is provided for quick tip text manipulation as seen in the sliders demo: this.quickTipUpdate([rgb]);

More information under the QuickTip header of this chapter.

CheckBox class
PROTOTYPE PROPERTIES

width:skin.checkBox.width
height:skin.checkBox.height
fill:skin.checkBox.fill
stroke:skin.all.stroke
caption:null

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
text<text> element
checkmarkreference to symbol's shape element

Example:

var checkBox1 = new pergola.CheckBox();
checkBox1.build({
  owner: optionsPanel.check,
  parent: optionsPanel.control,
  x: 53,
  y: 140,
  checked: false,
  caption: {
    position: "right",
    text: {
      x: 34,
      y: 11,
      textNode: "Option 3",
      "pointer-events": "none"
    }
  }
});

An example with explanations on how a group of check boxes can be managed by a panel can be found in the Panel chapter under the PANEL WITH RADIO BUTTONS/CHECK BOXES header.

The owner property
Reference to an object. In the case of the demo example this object is itself a property of a panel object and its purpose is to establish a level of communication and manage the state of its check-boxes. From the demo we can see that we defined one property only for the owner object, a function, while a “checkboxes” property is added dynamically. This function is not treated as a User Function, meaning that we don't dispose of the checks carried out on User Functions. The function is simply called. Instead, the check-boxes themselves can have User Function properties that will be treated as such.

The checked property
Boolean. Designates the initial state of the check-box, true = checkmark visible.

The caption property
Object. Optional. This object can have exactly 2 properties: position, which gets "left" (default) or "right"; text, which gets a text object. The property caption mutates to become a reference to the <text> element.

ChildDoc class

An instance of this class is designed to be a dependency only and represents the SVG contents of some other object, which are likely to be updatable as well as to be the target of transformations, whether entire documents or dynamic groups of elements like lists. The classes Window and Combobox automatically assign a new ChildDoc instance to their childDoc property.

None of the prototype properties of this class can be overridden. But some may be useful to query. One that deserves particular attention is the custom CTM property:

CTM: {a:1, b:0, c:0, d:1, e:0, f:0}

By querying the properties of this object you will get reliable and consistent results across the SVG implementations. The point here is not to debate who was right and who was wrong with their implementations, but rather to know that if you need to add sophisticated mapping tools, for example, in a window's tool-bar you can safely consider these values accurate:

var e = myWindow.childDoc.CTM.e;

returns the same value everywhere.

Another property which can be useful to query is scaleFactor. These properties, together with some other listed in the ChildDoc properties sheet, are read only.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

port<svg> element
transformable<g> element

Example from the Window build() method:

this.childDoc = new pergola.ChildDoc(this.id + "_childDoc");
this.childDoc.build({
  owner: this,
  parent: this.control,
  x: 1,
  y: 1
});

Inserting contents to a window means appending them to the myWindow.childDoc.transformable group, which is the target of transformation tools including the scrollbars and all the window's resizing commands, whenever these latter happen to affect the position of the contents.

It is important to know that we can append elements elsewhere in the window pane or to the childDoc port and that, by doing so, these elements will not be subject to transformations. This can be useful for example for map or other legends which we may prefer to keep a static position in the window's pane. Before doing anything of that kind we will need to know the structural elements of a window, found under the Window header in this chapter. It's also worth taking some time to analyze the DOM tree generated by the windows examples, with those implementations that offer this possibility. While doing that, it's good to remember that many of the object's elements are referenced, but not all.

The owner property
Reference to an existing object. Required.

Note: wherever available, the MutationEvents feature is used for contents update of the childDoc transformable group. The "DOMNodeInserted" and "DOMNodeRemoved" events are registered.

Unfortunately this is not implemented in ASV 3. If we target, or simply care about this implementation we need to explicitly call the appropriate method if, and after, we append contents manually:

if (!pergola.mutationEvnt) {
  myWindow.getBBoxOnMutationEvent(myWindow.childDoc);
}

If the contents are appended (or updated) through some function, as is for example the case with the loadDocument() method of the Load class, the function will execute the call.

ColorBoxSelector class
PROTOTYPE PROPERTIES

width:skin.colorBoxSelector.width
height:skin.colorBoxSelector.width
fill:skin.colorBoxSelector.width
stroke:skin.all.stroke

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
rect<rect> element

Example:

var g = $C({element:"g", transform:"translate(100 50)", appendTo:pergola.user});

var mySelector = new pergola.ColorBoxSelector();
mySelector.build({
  parent: g,
  caption: {
    x: -6,
    y: 15,
    'text-anchor': "end",
    'pointer-events': "none",
    textNode: "Select color"
  },
  target: pergola.background,
  fn: function() {
    var cp = pergola.colorpicker;
    var user = cp.user;
    var color = cp.color;
    user.fill = color;
    user.rect.setAttributeNS(null, "fill", color);
    user.target.fill = color;
    user.target.rect.setAttributeNS(null, "fill", color);
  }
});

This class is specifically designed to use the pergola.colorpicker, an instance of the ColorPicker class, built by the system.

The caption property
Object. Optional. This object can have exactly 2 properties: position, which gets "left" (default) or "right"; text, which gets a text object. The property caption mutates to become a reference to the <text> element.

The event handler of this class invokes the pergola.colorpicker.init() method, passing as unique parameter an object with two properties: user, which gets assigned the this keyword designating the instance, and color, which gets assigned the fill property of the instance.

More information under the ColorPicker header in this section.

ColorPicker class

System component. Pergola creates an instance of this class named pergola.colorpicker.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

None of the properties of this class should be overridden. The correct functioning of the colorpicker is not guaranteed otherwise.

The class has has a property named isUp which is extensively used in event handlers:

if (pergola.ColorPicker.isUp) return;

or:

if ((this != pergola.colorpickerDialog && pergola.ColorPicker.isUp) || (this != pergola.notification && pergola.notification.isUp)) return;

You will not normally need to use tests of this type in your User Functions, but you may/should if you write specific event handler functions for your own objects, i.e. objects which are not instances of any of the Pergola classes.

An object initializes the colorpicker by invoking its init() method, passing as unique parameter an object with two properties. Example:

pergola.colorpicker.init({
  user: ...,
  color: ...
});

The user property
Object. Designates the object which is requiring the colorpicker.

The color property
String. The initial color in hexadecimal format, ex: "#FF0000". Note: if you assign a fill or any other property designating a color, make sure its value is in hex format, or use the pergola.colorConvert(param) method, where param is a color in any format (see the Skins chapter).

ComboBox class
PROTOTYPE PROPERTIES

width:skin.comboBox.width
height:skin.comboBox.height
scrollBarSize:skin.comboBox.scrollBarSize
listItemHeight:skin.comboBox.listItemHeight
listMargin:skin.comboBox.listMargin

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

control<g> element
childDocChildDoc instance
listGroup<g> element

Example:

myPanel.comboBox = new pergola.ComboBox(myPanel.id + "_comboBox");
myPanel.build({
  owner: myPanel,
  parent: myPanel.control,
  x: 160,
  y: 80,
  list: myList,
  fn: "listItemFunction"
});

Combo-boxes are generally used to propose multiple choices to the user, the selection being validated by double-click on a list item or a panel's OK button, while Cancel and close buttons cancel the selection. This layout is used in the combobox demo where the combobox is a property of the previously built panel.

Note that this version of Pergola does not provide a class for creating layouts for windows and panels.

The owner property
Reference to an existing object. Required.

The list property
Object. Either existing or one created on the fly. The properties of this object are themselves objects with at least one property named string and any other properties that you may need to be processed by your User Function. The creation of ListItem instances is an automatic process. More information under the ListItem header in this chapter.

The fn property
User Function. Note that you cannot set the ev and target properties for the combo-box. The events are already registered on list items, a single click selects the item and a double-click validates the selection. A property named selection is assigned dynamically on selection.

CommandButton class

This is a subclass of ToolButton class, itself a subclass of Button. Its intent is simply to override some visual properties of the regular button in order to give its instances a particular and consistent appearance.

PROTOTYPE PROPERTIES

width:skin.commandButton.width
height:skin.commandButton.height

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

A command button is structurally equal to a regular button. See the Button class in this chapter.

The command buttons category in Pergola includes these types: “close” buttons for windows, panels or other objects; “minimize” buttons; “full” buttons. You can extend the command buttons category to other button types that you think appropriate.

Example from the Panel build() method:

this.close = new pergola.CommandButton(this.id + "_close");
this.close.build({
  owner: this,
  parent: this.control,
  x: (this.width - this.close.width - 6),
  y: 7,
  extra: {
    rx: 3
  },
  symbol: {
    symbol: pergola.symbols.winClose,
    x: 5,
    y: 4
  }
});

See the Button class for the properties assigned in the call.

DialogButton class

This is a subclass of Button class. It overrides some visual properties of the regular button, as well as the build() method.

PROTOTYPE PROPERTIES

rx:skin.dialogButton.rx
width:skin.dialogButton.width
height:skin.dialogButton.height
textFill:skin.text.fill
textFillInverse:skin.text.fillInverse
hasTextEffect:true
extra:{}

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
mask<rect> element
button<rect> element
text<text> element

Example of a standalone dialog button:

var button1 = new pergola.DialogButton();
button1.build({
  parent: pergola.user,
  x: 200,
  y: 250,
  string: "Start",
  ev: "click",
  target: progressBar1,
  fn: "startProgress"
});

For panel dialog buttons the procedure is simplified. We only need to set two properties (either one or both):

var panel = new pergola.Panel("demo panel");
panel.build({
  ...,
  okButton: {},
  cancelButton: {}
});

The okButton and cancelButton above are volatile properties, they do not become properties of the panel instance. If they are not explicitly set then the panel will have no dialog buttons. If set, they can only have two properties: string and marginRight. They default to “OK” and 260px, “Cancel” and 230px, for the okButton and the cancelButton respectively.

The owner property
Reference to an existing object. If no User Event and User Function have been declared this property is required.

The text property
String. The button's text.

Note: contrary to regular buttons, the text is pre-formatted and you cannot define a text property in the call to the Build() method.

Dragarea class

System component. Pergola creates an instance of this class named pergola.dragarea.

PROTOTYPE PROPERTIES

width:pergola.width()
height:pergola.height()
fill:"none"
updateCoordinates:true
cursor:"default"

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

rect<rect> element

Pergola builds a drag-area for use by any object. It is activated by calling the pergola.dragarea.activate() method.

pergola.dragarea.activate(object);

The function expects 1 parameter of type “object”. This object has the following properties:

fnfunction name
handlethe object making the call
targetthe element to drag
offsetXoffset of evt.clientX to the element's x coordinate
offsetYoffset of evt.clientY to the element's y coordinate
updateCoordinatesits value determines if the element's coordinates are to be updated after release

The properties of the object become properties of pergola.dragarea. Let's see them in detail:

The fn property
Function. This should be the superclass prototype method moveXY() or moveByTranslation() of the pergola.Class.prototype, appropriately. For an <svg> element for example it will be moveXY(), while for a <group> element it will be moveTranslate().

The handle property
Object. Self reference. Typically, the this keyword in your event handler function. In any case the object requesting the use of the drag-area.

The target property
Reference to the element to drag. Not necessarily an element of the handle object.

The OffsetX and OffsetY properties
Number. The offsets of the event to the coordinates of the single element or container. We've seen that all Pergola classes have x and y prototype properties. It is important to remember that for all classes x and y properties always relate to the position of the container. They never relate to any particular rectangle element of the object. If you are moving any object that is not an instance of any of the Pergola classes and for which you have not set those properties, then you will have to write a specific function for moving your object.

The updateCoordinates property
Boolean. Specifies whether the element's coordinates are to be updated after release. Defaults to true. For scrollbars and sliders, for example, which use more specialized transformation methods, this property is set to false.

You can create other instances of Dragarea and define its instance methods if you need it.

Frame class
PROTOTYPE PROPERTIES

width:200
height:200
rx:skin.frame.rx
fill:skin.frame.fill
'fill-opacity':skin.frame['fill-opacity']
stroke:skin.frame.stroke
'stroke-opacity':skin.frame['stroke-opacity']
filter:"none"
extra:{}

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

rect<rect> element

Pergola windows and panels use a frame. A window frame can be resized in all directions by pulling its edges and corners. The methods used in this version of Pergola are designed to resize windows only. Panels in this version are only used for dialogs and cannot be resized.

Example from the build() method of the Window class:

this.frame = new pergola.Frame(this.id +"_frame");
this.frame.build({
  owner: this,
  parent: this.control,
  width: this.width,
  height: this.height,
  filter: "none"
});

Example from the build() method of the Panel class:

this.frame = new pergola.Frame(this.id + "_frame");
this.frame.build({
  owner: this,
  parent: this.control,
  width: this.width,
  height: this.height,
  fill: "#F4F4F4",
  'fill-opacity': 1,
  'stroke-opacity': 1
});

The owner property
Reference to an existing object. Required.

Layout class

This class is experimental and may be subject to radical changes in future versions. We will try to ensure retro-compatibility as much as possible.

All the prototype properties of this class define methods.

PROTOTYPE PROPERTIES

table method
combobox method
colorpicker method
tabbed method
message method

All we have to do is set the layout property for the object requiring it. The property gets an object where we define properties depending on the type of the layout.

Let's examine first the properties common to all methods of the class.

COMMON
The type property
String. Required. The value of this property must be the name of an existing method of the Layout class.

The x, y, width and height properties
Number. Optional. If not set, these will be set to the container's available space.

Other properties are set depending on the layout type:

combobox
The list property
Array. Required. All the elements of this array are strings.
message
No properties. Note that the pergola.notification system object uses this layout. To show messages to the user using this object we simply invoke the $M method: $M("your message"). This box mimics the window.alert() in that it is a preemptive dialog, but doesn't stop the script.
tabbed
The tabs property
Object. Required. The properties of this object have arbitrary names and are themselves objects. A Tab instance is created for each of them. The Tab properties are enumerated under the Tab header in this chapter.
colorpicker
No properties. See the ColorPicker class in this chapter.
table
The rows and cols properties
Number. Required. The properties define the number of rows and columns respectively.

The spacing property
Number (inherited units only). Optional. The spacing between cells.

The attributes property
Object. Optional. Define here any properties relating to attributes of the <rect> element. They will apply to the table cells.

Note: the x, y, width, height and spacing properties for the table layout, only take values of type Number. You cannot use units (string) as no conversion is done in expressions. The User Space units apply.

A simple example, the combobox layout in panels:

var myPanel = new pergola.Panel("my panel");
myPanel.build({
  ...,
  layout: {
    type: "combobox",
    y: 40,
    list: myList
  }
});

The table layout in a panel:

var myPanel = new pergola.Panel("my panel");
myPanel.build({
  ...,
  layout: {
    type: "table",
    x: 12,
    y: 12,
    rows: 2,
    cols: 3,
    spacing: 4,
    attributes: {
      fill: "#FFFFE8",
      stroke: "#A0A0A0",
      'stroke-width': 1			// redundant
    }
  }
});

Example of layout in some other container:

myObject.layout = new pergola.Layout();
myObject.layout.build({
  owner: myObject,
  parent: parentNode,
  type: "table",
  x: Number,
  y: Number,
  rows: Number,
  cols: Number,
  spacing: Number,
  attributes: {
    ...
  },
  width: width,
  height: height
});

Note: the table layout is an alternative to the technique of using the <foreighObject> element in conjunction with the HTML Node Buider, and although it doesn't provide the formatting options of an html table, it is likely to remain a good alternative for some time since at this stage only Firefox renders the html table fully, the webkit implementations render it partially (no borders, no background fill, no cellpadding, no cellspacing) and Opera doesn't render it. As for ASV, <foreighObject> wasn't implemented.

ListItem class
PROTOTYPE PROPERTIES

fill:skin.listItem.fill
maskFill:skin.listItem.maskFill
textFill:"black"
textFillInverse:"white"

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
button<rect> element
text<text> element

An instance of this class is designed to be a property of another object. Pergola combo-boxes use ListItem instances. The ListItem build() method expects two parameters and in this respect it constitutes an exception (as the class MenuItem also does).

Example from the ComboBox build() method:

for (var prop in this.list) {
  this[prop] = new pergola.ListItem(this.id + '_' + prop);
  this[prop].build({
    owner: this,
    parent: this.listGroup,
    text {
      x: this.listMargin,
      y: parseInt(this['font-size']) + 4,
      textNode: this.list[prop].string,
      'pointer-events': "none"
    }
  });
}

Information about the list property under the ComboBox header in this section.

The owner property
Reference to an existing object. Required.

The text property
Object. Required. This property is an object where you can set any SVG text attribute using SVG syntax. The property textNode is a pseudo-attribute name and gets a string; a text node will be created automatically.

Note: the text property mutates. It ceases to be the original object to become a reference to the object's <text> element.

Load class

This class does not inherit from the pergola.Class superclass. Its function is to load an SVG document from a local or remote URL. This class has prototype methods only.

PROTOTYPE METHODS

loadDocument method
addDocument method

Example from load-progress.svg demo:

var newWin = new pergola.Window("Isle of Man");
newWin.build();

var panel = new pergola.Panel();
panel.build({
  title: "LOAD PANEL",
  x: 10,
  y: 40,
  height: 150,
  width: 450,
  fill: "#F4F4F4",
  okButton: {
    text: "Load"
  },
  target: newWin,
  fn: "load",
  display: "block"
});

panel.load = function(evt) {
  var t = this.target;
  t.load = new pergola.Load();
  t.load.loadDocument('Isle_of_Man_topographic_map-en_rev.svg', t, t.progress);
}

The new file, an SVG map, is wrapped into the new window that we purposely created and which is the target of the button.

The loadDocument() method expects three arguments:

url
String. The URL of the file to be loaded. When loading a new document the browsers' security restrictions apply. The document must be located in the same domain, whether local or remote.

container
Reference to an existing node. For a window we just pass the window object itself; the new document will be appended to the childDoc.transformable group.

progress
Object. Optional. It designates an existing progress bar object. Windows have a built-in Progress instance named progress.

The addDocument() method is a dependency of loadDocument() and is not designed to be invoked directly.

Menu class
PROTOTYPE PROPERTIES

'font-size':skin.menu['font-size']
fill:skin.menu.fill
textFill:skin.text.fill

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
button<rect> element

Menus are not top level objects. They normally are properties of some object. A typical menu environment consists of an object, its menu bar, its menus and user function:

var myObject = {
  menubar: new pergola.Menubar(),
  menu: {
    menu1: new pergola.Menu("Menu 1"),
    menu2: new pergola.Menu("Menu 2")
  },
  message: function(evt) {...}
};

In this example we can see the advantages of the objects construction technique used in Pergola, explained in the INTRODUCTION, under the Use header, although they are not immediately evident from this simple example. The menubar, menu1 and menu2 properties are assigned new instances from their respective classes. Another way to see things is that the structure of the menu is defined here.

Note also that the function assigned to the message property is a simple one defined in a convenient place. The real User Function properties are set for each individual menu item. They happen to point to this same function for the sake of the demo's simplicity. In a real situation we could/would probably have them reference different functions, depending on what action each menu item is supposed to carry out.

Next the menubar gets built:

myObject.menubar.build({
  x: 200,
  y: 200
});

Note that the Window class build() method automatically builds a menubar if the menu property has been set in the call. See the windowsfull.svg example.

Finally, we build the menus. Let's see the more interesting menu1 example with its submenus:

myObject.menu.menu1.build({
  owner: myObject,
  parent: myObject.menubar.group,
  title: myObject.menu.menu1.name,
  items: {
    item1: {
      string: "Menu Item #1",
      active: true,
      check: true,
      fn: myObject.message
    },
    item2: {
      string: "Menu Item #2",
      active: true,
      check: false,
      fn: myObject.message
    },
    food: {
      string: "Food",
      active: true,
      submenu: {
        items: {
          organic: {
            string: "Organic (OOS)",
            active: false,
            fn: myObject.message
          },
          fruits: {
            string: "Fruits",
            active: true,
            submenu: {
              items: {
                bananas: {
                  string: "Bananas",
                  active: true,
                  check: false,
                  fn: myObject.message
                },
                pears: {
                  string: "Pears",
                  active: true,
                  check: false,
                  fn: myObject.message
                },
                cherries: {
                  string: "Cherries",
                  active: true,
                  check: false,
                  fn: myObject.message
                },
                plums: {
                  string: "Plums",
                  active: true,
                  check: false,
                  fn: myObject.message
                }
              }
            }
          },
          vegetables: {
            string: "Vegetables",
            active: true,
            submenu: {
              items: {
                carrots: {
                  string: "Carrots",
                  active: true,
                  check: false,
                  fn: myObject.message
                },
                potatoes: {
                  string: "Potatoes",
                  active: true,
                  check: false,
                  fn: myObject.message
                },
                beans: {
                  string: "Beans",
                  active: true,
                  check: false,
                  fn: myObject.message
                },
                tomatoes: {
                  string: "Tomatoes",
                  active: true,
                  check: false,
                  fn: myObject.message
                }
              }
            }
          }
        }
      }
    },
    item4: {
      string: "Menu Item #4",
      active: true,
      fn: myObject.message
    }
  }
});

The owner property
Except for a window menu this property is required.

The title property
String. This class gives the possibility to set the title property to which we can assign a string different from the name. In our demo case we are OK with the menu name and we made the title point to that.

The items property
Object. The properties declared here are objects and each will generate a new MenuItem instance. The properties of these objects are reviewed under the MenuItem header in this chapter.

It is not interesting for us to know the mechanism used to build the menu and its submenus, but for those of us who might be interested we can resume by saying that the menu declares a list property as a new MenuList instance. The menu's items property becomes then a property of the list and for each of its properties a new MenuItem instance is created. If the submenu property is set here, then the build() method is called recursively, or more precisely in parallel. The submenu is not a concrete object, rather, a new MenuList instance is created and executed in its own scope.

Menubar class
PROTOTYPE PROPERTIES

width method
height:skin.menuBar.height
fill:skin.menuBar.fill
advanceX:0

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
bar<rect> element

Example:

var myObject = {
  menubar: new pergola.Menubar(),
  ...
};

myObject.menubar.build({
  x: 200,
  y: 200
});
MenuItem class

This class is designed to be a dependency of a MenuList instance. An instance of this class is not created explicitly. See also the Menu and MenuList classes in this chapter. The MenuItem build() method expects two parameters and in this respect it constitutes an exception (as the class ListItem also does).

PROTOTYPE PROPERTIES

height:skin.menuItem.height
fill:skin.menuItem.fill
textFill:"black"
textFillInverse:"white"

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
button<rect> element
text<text> element
checkmarkreference to symbol's shape element
arrowreference to symbol's shape element

A checkmark is built if the check property in the call is set to true. The property points directly to the symbol's element itself, not to the symbol's group. Likewise, an arrow is built if the submenu property is set in the call. The property points directly to the symbol's element itself, not to the symbol's group. This is to allow direct addressing of those symbols in your functions for fill operations without the need to use node properties. We know in effect that a Pergola symbol has a group container and that to set a painting attribute for a container does not resolve if done dynamically, although ideally it should.

As we have seen from the menu example in the Menu class above, a menu item is an object defined as a property of the items property (object). The following properties can be assigned:

item1: {
  string: "Menu Item #1",
  active: true,
  check: true,
  fn: menu.message
}

The string property
String. Required. The visible string of the menu item.

The active property
Boolean. If false the menu item is grayed out and inactive.

The check property
Boolean. If assigned, if true the menu item has a checkmark, if false the menu item has a checkmark but its initial display value is “none”.

The fn property
User Function. See the USER EVENTS AND FUNCTIONS chapter for the possible values of this property. We do not assign ev or target properties for a menu item.

The submenu property
Object. We treat this as if it were a menu, with the only difference that it has a unique items property. We do not assign owner and parent properties.

A menu item which is a submenu should never have a fn property since its only function is to open the popup list. A checkmark doesn't make sense either.

Menu items get built by an automatic process. We never make explicit calls to the MenuItem build() method.

MenuList class

This class is designed to be a dependency of an Menu instance. An instance of this class is never created explicitly. See also the Menu class in this chapter.

PROTOTYPE PROPERTIES

height:0
margin:skin.menuList.margin
advanceY:0

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element

The Menu build() method defines a list property for each menu instance and assigns to it a new MenuList instance. The items object defined in the call becomes then a property of the list and for each of its properties a new MenuItem instance is created.

Panel class

System component.

CLASS PROPERTIES

isDialogUp:false

PROTOTYPE PROPERTIES

type:"dialog"
x:skin.panel.x
y:skin.panel.y
width:skin.panel.width
height:skin.panel.height
fill:skin.panel.fill
footerFill:skin.panel.footerFill
opacity:1
margin:skin.panel.margin
footerHeight:skin.panel.footerHeight

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

control<g> element
frameFrame instance
topBarTopBar instance
closeCommandButton instance
okDialogButton instance
cancelDialogButton instance

The prototype property type with initial value "dialog" can be overridden with the value "basic".

The “basic” type produces a simple panel, with no dialog buttons. The simplePanel example:

var panel = new pergola.Panel("basic panel");
panel.build({
  type: "basic",
  title: "BASIC PANEL",
  filter: "none",
  display: "block"
});

A basic dialog is really made of just a Frame instance, but like all panels it is appended to the pergola.dialogs group and hence, always on top.

The "dialog" type produces a generic panel with configurable dialog buttons. The dialogPanel.svg example:

var panel = new pergola.Panel("demo panel");
myPanel.Build({
  type: "dialog",
  title: "DIALOG PANEL",
  x: 100,
  y: 100,
  width: 400,
  height: 360,
  okButton: {},
  cancelButton: {}
});

We can choose to have 0, 1 or 2 dialog buttons. Although the buttons have x and y properties, for a dialog panel we can set instead a marginRight property for a consistent appearance of panels. The default values are 260px for the ok button and 130px for the cancel button. We cannot override other dialog buttons properties here since their precise intent is to have a consistent look, which you can tweak in the skin. Also, contrary to regular buttons, the text style is preset and cannot be modified, so we simply assign the string property. The behavior of the buttons, through the panel events, is flexible and configurable as we will see.

As we can see from the numerous panel examples, we can use several layouts with panels. More information under the Layout class in this chapter.

PANEL WITH RADIO BUTTONS/CHECK BOXES

These objects can be used as standalone of course, but let's see how to enable a panel to handle radio buttons and/or check boxes. The code below is from the Radio-CheckButtons.svg example.

To start off we assign an object (for each group radio buttons and/or check-boxes) to an arbitrary property that we set in the call for creating the Panel instance.

For the radio buttons:

var optionsPanel = new pergola.Panel("Options Panel");
optionsPanel.Build({
  ...,
  radio: {
    selection: null,
    fn: function() {$M(this.selection.text + " is selected.")}
  }
});

The radio property will be the owner object of one group of radio buttons:

var radioButton0 = new pergola.RadioButton();
radioButton0.Build({
  owner: optionsPanel.radio,
  parent: optionsPanel.control,
  x: 60,
  y: 80,
  caption: {
    position: "right",
    text: {
      textNode: "Option 1",
      "pointer-events": "none"
    }
  },
  isDefault: true
});

The property can be given any name and the object assigned to it is a placeholder where information about the radio buttons is stored. A new property options is dynamically added to the object. It is an object and its properties reference the radio button objects. The selection property is dynamically updated and points to the current selection.

For the check-boxes:

optionsPanel.Build({
  ...,
  check: {
    fn: function() {...}
  }
});

The check property will be the owner object of one or more check boxes:

var checkBox1 = new pergola.CheckBox();
checkBox1.Build({
  owner: optionsPanel.check,
  parent: optionsPanel.control,
  x: 53,
  y: 140,
  checked: false,
  caption: {
    position: "right",
    text: {
      textNode: "Option 3",
      "pointer-events": "none"
    }
  }
});

The property can be given any name and the object assigned to it is a placeholder where information about the check boxes is stored. A new property checkboxes is dynamically added to the object. It is an object and its properties reference the check box objects.

The functions assigned to the fn property of the radio and check objects are not treated as User Functions, meaning that we don't dispose of the checks carried out on User Functions. The functions are simply called. Instead, the individual objects themselves can have User Function properties. If we set both, things can get quite interesting, and that is left to your imagination.

PopupList class

This class is designed to be a dependency of a Selector instance and can be used by other types of selectors. An instance of this class is never created explicitly. See also the Selector class in this chapter.

PROTOTYPE PROPERTIES

width:skin.popupList.width
listItemHeight:skin.popupList.listItemHeight
listMargin:skin.popupList.listMargin

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
listGroup<g> element
"item name"PopupListItem instances

The Selector build() method defines a popList property for each menu instance and assigns to it a new PopupList instance. The list object defined in the call becomes then a property of the PopupList instance and for each of its properties a new PopupListItem instance is created.

PopupListItem class

This class is a subclass of the ListItem class and its only purpose is to override the event handler.

PROTOTYPE PROPERTIES

See the ListItem class.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

See the ListItem class.

Preview class
PROTOTYPE PROPERTIES

rx:skin.preview.rx
width:skin.preview.width
height:skin.preview.height
fill:skin.frame.fill
stroke:skin.frame.stroke
opacity:skin.preview.opacity

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element

This class is designed to make previews of windows. The process of creating a new instance is automatic. The preview is appended to the window tab group in the taskbar. The preview build() method makes a clone of the window's childDoc. The preview is updated when the window contents change.

Progress class
PROTOTYPE PROPERTIES

width:skin.progress.width
height:skin.progress.height
fill:skin.progress.fill
stroke:skin.progress.stroke
statusFill:skin.progress.statusFill
type:"bar"
extra:{}

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
rect<rect> element
status<rect> element

Note: due to the limitations of the getURL() method in respect to the XMLHttpRequest class the status progressive bar is replaced by the word “Loading” in IE + ASV.

Example:

var progressBar1 = new pergola.Progress("progressbar1");
progressBar1.Build({
  owner: pergola,
  x: 200,
  y: 200,
  timerString: 'progress()'
});

The owner property
Reference to an existing object. Required. The progress bar of the progress.svg demo has no context and we should never need to use it in this way; we can safely assign the value pergola to the owner property.

The timerString property
String. Required. This property gets the function call, including the parenthesis, as a string for use by the Timer class methods. It is not called fn in order to avoid confusion with User Functions.

Due to issues with clearInterval() in IE8, the impossibility to clear intervals in a non-event context, this browser will show a stop button to prevent it from continuing indefinitely and eventually provoke a stack overflow. There probably are mega hacks to use as workarounds, but the lesson we learn from all this is to avoid using manual progress bars with no functionality, like in the demo. In any case it doesn't make much sense to use a progress bar in a non-event context.

A more realistic example is the window's load progress bar:

this.progress = new pergola.Progress(this.id + "progress");
this.progress.Build({
  owner: this,
  parent: this.commands,
  y: this.topBar.height - 1,
  width: this.topBar.width - 2,
  height: 1,
  fill: "none",
  stroke: "none"
});

See the Load class in this chapter for its use. Note that in local mode the download goes too fast for the progress bar to be actually seen.

QuickTip class

System component.

PROTOTYPE PROPERTIES

'font-size':skin.quickTip['font-size']
rx:skin.quickTip.rx
fill:skin.quickTip.fill
stroke:skin.quickTip.stroke
textFill:skin.quickTip.textFill
delay:skin.quickTip.delay

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
textGroup<g> element
text<text> element
bubble<rect> element

The classes implementing quick-tips are: Button, ToolButton, CommandButton and Slider.

Pergola has a library of reusable quick-tips. They are defined as properties of the pergola.quicktip object, found in the qtips.es file. Several objects can reference and use the same quick-tip –meaning that there is only one instance of a quick-tip. This is possible because, quick-tips being shown one at a time, there cannot be coordinates conflicts. We reference a quick-tip from the library by referencing its name as a string:

  quickTip: "lensTool"

A quick-tip can also be defined on the fly. In this case it will be an object where we can define the following properties:

The tip property
String or Array. Required. When assigning a string, you can use the "|" vertical bar character as line separator.

quickTip: {
  tip: "My name is Bond,|James Bond"
}

quickTip: {
  tip: [
    "My name is Bond,",
    "James Bond"
  ]
}

The x and y properties
Number or String. Optional. Assign these properties for a quick-tip to show at a fixed position, with no mouse tracking.

quickTip: {
  tip: "I'm fixed",
  x: 300,
  y: 200
}

The delay property
Number. Optional. The delay in milliseconds.

quickTip: {
  tip: "I show up instantly"
  x: 300,
  y: 200,
  delay: 0
}

If we need to implement quick-tips for an orphan object or user defined class we can follow these procedures:

For an orphan object:

var userObject = {};
var userObject.rect = $C({element:"rect", ... });

Case - using a quick tip from the library:

userObject.quickTip = "lensTool";
pergola.Class.protoype.quickTipFormat.call(userObject);

Case - defining a quicktip on the fly:

userObject.quickTip = {
  tip: "Hey, I'm a quick tip!"
 };
pergola.Class.protoype.quickTipFormat.call(userObject);

For user defined class add this line in its constructor function:

if (this.quickTip) this.quickTipFormat();

You can then use one of the methods described above when creating an instance.

For both situations you will then need to register the events and define a handler. This process is a little complex and its explanation is out of scope. For the courageous ones, the best to do is to peek and see how a Pergola class does it.

RadioButton class
PROTOTYPE PROPERTIES

fill:skin.radioButton.fill
stroke:skin.radioButton.stroke
dotFill:skin.radioButton.dotFill
cx:skin.radioButton.cx
cy:skin.radioButton.cy
r:skin.radioButton.r
dotR:skin.radioButton.dotR
caption:null

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
dot<circle> element

Example:

var radioButton0 = new pergola.RadioButton();
radioButton0.Build({
  owner: optionsPanel.radio,
  parent: optionsPanel.control,
  x: 60,
  y: 80,
  caption: {
    position: "right",
    text: {
      textNode: "Option 1",
      "pointer-events": "none"
    }
  },
  isDefault: true
});

An example with explanations on how a group of radio buttons can be managed by a panel can be found in the Panel chapter under the PANEL WITH RADIO BUTTONS/CHECK BOXES header.

The owner property
Reference to an existing object. Required. In the case of the demo example this object is an arbitrarily named property of a panel object and its purpose is to establish a level of communication and manage the state of its radio buttons. We set exactly 2 properties for this object: selection and fn. The initial value of selection is null. The property will then point to the radio button that has its isDefault property set to true.

The caption property
Object. Optional. This object can have exactly 2 properties: position, which gets "left" (default) or "right"; text, which gets a text object. The property caption mutates to become a reference to the <text> element.

The isDefault property
Boolean. The button that has this property set to true is referenced by the owner.selection property.

Scrollbar class
PROTOTYPE PROPERTIES

fill:skin.scrollBar.fill
stroke:skin.scrollBar.stroke
horCradleFill:skin.scrollBar.horCradleFill
vertCradleFill:skin.scrollBar.vertCradleFill
rx:skin.scrollBar.rx
size:skin.scrollBar.size
min:0
extent:0
scrollType:"linear"
initial:"start"
sliderClearance:2
step:10

Note: This version of Pergola only implements the "linear" value for the scrollType property. The properties min, extent, initial and sliderClearance must not be overridden. The property step relates to the slider buttons and can be overridden.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
cradle<rect> element
sliderScrollSlider instance
backScrollButton instance
fwdScrollButton instance

This class has buildH() and buildV() methods for horizontal and vertical scrollbars respectively.

Example, the combo-box horizontal scrollbar:

this.hScrollBar = new pergola.Scrollbar(this.id +"_hScrollBar");
this.hScrollBar.buildH({
  owner: this,
  parent: this.control,
  scrollTarget: this.childDoc,
  symbol: pergola.symbols.arrow.small,
  step: this.listItemHeight + 2
});

The scrollTarget property
Reference to object, element or container. The buildH() and buildV() methods assign appropriate prototype methods to the fn, sliderFn, backScrollFn and fwdScrollFn properties. These methods are designed to manipulate the CTM property of a childDoc, and the translations apply to its transformable group. For windows, combo-boxes, or any object of your construction that uses a ChildDoc instance all we have to do is assign the object's childDoc to the scrollTarget property. For windows and combo-boxes the process is automatic.

If you assign a simple element or a container to the scrollTarget property, then you must also assign these instance properties in the call:

fna function for the cradle
sliderFna function for the slider
backScrollFna function for the back button
fwdScrollFna function for the forward button

with appropriate custom functions. This can quickly get quite complicated. The use of a childDoc is strongly recommended, even if just for simple contents, as the associated prototype methods for transformation are guaranteed to work correctly and yield equal results in all implementations, however deeply nested the element may be.

From this we understand that Pergola scrollbars are system tools, not simple sliders to move some object around or set some value. Instead, a regular slider, an instance of the Slider class, is designed for that, if needed.

The symbol property
Reference to symbol object in library. The symbol is used for the back and forward buttons. This implies that the symbol object must have up, down, left and right properties, as is the case for the arrow.small symbol. The scrollbars of a window instance, for example, use arrow.large which also has those variants.

ScrollSlider class

This class is a dependency of the Scrollbar class and is designed to produce slider objects specifically for that class. Not to be confused with the Slider class which produces standalone sliders.

PROTOTYPE PROPERTIES

rx:skin.scrollSlider.rx
fill:skin.scrollSlider.fill
minSize:16

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
button<rect> element

ScrollSlider instances are automatically created by a Scrollbar object. Just for the record, this is how it's done from the Scrollbar buildH() method:

this.slider = new pergola.ScrollSlider(this.name +"_slider");
this.slider.Build({
  parent: this.group,
  pos: 0,
  x: this.scrollButtonSize + this.sliderClearance / 4,
  y: 2,
  width: 0,
  height: this.size - 4,
  stroke: pergola.presentationAttributes.scrollSlider.stroke.horizontal,
  scrollTarget: this.scrollTarget.CTM
});

The values of these properties have a direct incidence on the visual design of the scrollbar and its slider. Tweaking the values to customize the look means to think globally, i.e. to take into account several properties of the horizontal and vertical scrollbars and their sliders. If the new values are appropriate the correct functioning of the scrollbar will not be affected.

The scrollTarget property
Object. Required. The slider needing a simpler logic than the cradle, it can act directly on the CTM of the scrollTarget. Remember that the custom CTM is an instance property of the childDoc object.

Selector class
PROTOTYPE PROPERTIES

skin.selector.stroke
'font-size':skin.selector['font-size']
fill:skin.selector.fill
stroke:
'stroke-width':skin.selector['stroke-width']
maskFill:skin.selector.maskFill
rx:skin.selector.rx
width:skin.selector.width
height:skin.selector.height
listItemHeight:20

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
button<rect> element
text<text> element
popListPopupList instance

Example:

mySelector = new pergola.Selector("My Selector");
mySelector.build({
  parent: g,
  list: [...],
  index: 0,
  caption: {
    x: -9,
    y: 15,
    'text-anchor': "end",
    'letter-spacing': 1,
    kerning: 0,
    'pointer-events': "none",
    textNode: "Size"
  },
  target: ...,
  fn: function() {...}
});

The list property
Array. Required. All its elements are of type String.

The index property
Number. Required. The list element to show by default.

The caption property
Object. Optional. This object can have exactly 2 properties: position, which gets "left" (default) or "right"; text, which gets a text object. The property caption mutates to become a reference to the <text> element.

Separator class
PROTOTYPE PROPERTIES

width:skin.separator.width
height:skin.separator.height

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element

Where needed, a separator property is defined directly as a property of the object that requires one and is assigned a new Separator instance. See the menu example.

myObject.menu.menu2.build({
  ...,
  items: {
    ...,
    item3: {
      string: "Menu Item #3",
      active: true,
      fn: myObject.message,
      separator: new pergola.Separator()
    },
  ...
  }
}

Example of horizontal separator from the MenuItem build() method:

this.separator.Build({
  parent: this.group,
  type: "horizontal",
  y: this.height,
  width: owner.width
});

The type property
String. Required. Allowed values are "horizontal" and "vertical". For a vertical type we assign the x and height properties instead of y and width. There is no demo for a vertical separator.

ShadowSelector class

System component. Pergola creates an instance of this class named pergola.shadowSelector.

PROTOTYPE PROPERTIES

fill:skin.shadowSelector.fill
'fill-opacity':skin.shadowSelector['fill-opacity']
stroke:skin.shadowSelector.stroke
'stroke-width':skin.shadowSelector['stroke-width']
'stroke-dasharray':skin.shadowSelector['stroke-dasharray']
maskStroke:skin.shadowSelector.maskStroke

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
mask<rect> element
rect<rect> element

Pergola builds a shadow selector:

pergola.shadowSelector = new pergola.ShadowSelector("pergola_shadowSelector");
pergola.shadowSelector.build();

which is used by the lens tool. Since we cannot use two at the same time, the shadow selector is reusable. But you can make a new instance. On initialization these instance properties are set:

this.x = this.y = this.width = this.height = 0;

Their manipulation is correlated to evt.clientX and evt.clientY. The prototype methods used by the lens tool are specific. For any other usage the corresponding logic must be provided.

Slider class
PROTOTYPE PROPERTIES

'font-size':skin.slider['font-size']
shape:skin.slider.shape
size:skin.slider.size
type:skin.slider.type
numberOfSteps:4
initial:skin.slider.initial
extent:skin.slider.extent
showValue:skin.slider.showValue
rx:skin.slider.rx
fill:skin.slider.fill
stroke:skin.slider.stroke
cradleFill:skin.slider.cradleFill
cradleStroke:skin.slider.cradleStroke
cradleOpacity:skin.slider.cradleOpacity
slotStroke:skin.slider.slotStroke
slotSize:skin.slider.slotSize

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
cradle<rect> element
buttonGroup<g> element
button<circle> or <rect> element
text<text> element
quickTipobject – optional

This class has buildH() and buildV() methods for horizontal and vertical sliders respectively.

It is worthwhile to study all the examples in the sliders demo. Let's simply review the properties.

The shape property
String. The allowed values are "round" (initial) and "square".

The type property
String. The allowed values are "linear" (initial) and "discrete".

The numberOfSteps property
Number. If the value is an even number and the initial property is set to "middle", the value is converted to the first preceding odd number. Ex: 7 to 6.

The showValue property
Boolean. If true, the value tip is shown next to the button. Initial: false.

The initial property
String. Allowed values: "start"; "middle" (initial); "end". The initial position of the slider button.

Tab class
PROTOTYPE PROPERTIES

'font-size':skin.tab['font-size']
width:skin.tab.width
height:skin.tab.height
fill:skin.tab.fill
hasVisualEffects:false
pane:false

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
pane<group> element
button<path> element
text<text> element

Although we can build a single tab, tabs are rather designed to come in groups. Let's see the panelWithTabs example in the Examples/Panel folder. We first assign an object to the tabs property in the call to the Panel build() method, as shown in Panel class under the header PANEL WITH TABS in this chapter. Although the call to the Tab build() method is deferred, the properties of this object will be set there, and if we want to override any prototype properties of Tab we do that there. Then the Panel build() method will create a Tab instance for each property of the tabs object:

if (this.tabs) {
  this.tabAdvanceX = this.margin;
  for (var t in this.tabs) {
    var tab = this.tabs[t];
    if (tab.active) var active = t;
    this[t] = new pergola.Tab();
    for (var p in tab) this[t][p] = tab[p];
    this[t].Build({
      owner: this,
      parent: this.topBar.group,
      x: this.tabAdvanceX,
      y: this.topBar.height + 1,
      pane: true
    });
    this.tabAdvanceX += this[t].width;
  }
  this.topBar.group.appendChild(this[active].group);
}

First, the panel's property tabAdvanceX is assigned. After the construction of each tab its value is updated. This means that we can have tabs with different widths; if we have overridden the width of a particular tab, tabAdvanceX will be added the new value.

After the new Tab instance has been declared, it is made to inherit the properties set in the object.

The owner property
Reference to object. Required.

The pane property determines whether a pane shall be built for the tab. It is a prototype property and its initial value is false.

If you need to append your tabs to some object other than a panel you will easily adapt this code.

TabsDock class

System component. Pergola creates an instance of this class named pergola.tabsDock.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

closedGroup<g> element
group<g> element

The tabsDock is simply constituted of two <g> containers. It is appended to pergola.taskbar.group. The pergola.tabsDock.group is where the window tabs are appended and the pergola.tabsDock.closedGroup, which has the display attribute set to "none" and should always remain like that, is for storing tabs of closed windows.

If you need to create your own instance of a tabs dock follow this model:

myTabsdock = new TabsDock();
myTabsdock.build({
  parent: parentNode,
});

Note: the prototype method tabsOrganize() is designed to organize the pergola.tabsDock with window tabs only. If you build your own tabs dock you will also have to provide the logic for its organization.

Taskbar class

System component. Pergola creates an instance of this class named pergola.taskbar.

PROTOTYPE PROPERTIES

width:skin.taskBar.width
height:skin.taskBar.height
position:skin.taskBar.position
hasMenu:skin.taskBar.hasMenu
fill:skin.taskBar.fill
maskFill:skin.taskBar.maskFill
display:"none"

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
rect<rect> element

The pergola.taskbar display is set to "none" by default. Upon creation of a Window instance with its hasCommands property set to true (default), the taskbar's display is automatically set to "block". See the windows examples.

If ever you needed a taskbar other than the pergola.taskbar, you create one explicitly.

The taskbar demo example:

myTaskbar = new pergola.Taskbar("myTaskbar");
myTaskbar.Build({
  position: "bottom",
  hasMenu: false
});

The position property
String. Allowed values: "top" (initial) or "bottom".

The hasMenu property
Boolean. If true the tabsdock group will have an offset of 48 px from the left margin. Note that setting this property to true does not mean that the taskbar has a menu, it simply leaves a space for you to build one.

Due to inconsistencies across the implementations with browser resizing affecting objects with width or height value of "100%", the pergola.resize() method compensates for this and updates the width for the taskbar, background (rect and image) and dragarea. This only affects the pergola.taskbar instance. If you create your own Taskbar instance you will have to provide the logic for re-dimensioning.

Note: at runtime Pergola also creates a TabsDock instance, pergola.tabsDock. Its components are appended to pergola.taskbar.group and it is the container for the window tabs.

Timer class

This is an abstract class and is not subclassed to the pergola.Class superclass. It has 2 class properties and 4 prototype properties.

A timer is designed to call the window.setTimeout() and window.setInterval() methods independently or combined, depending on the presence of the properties delay and frequence in the the call to its initializer method initCall().

The timer class clears its control array in the background as soon and whenever all activity of its instances is cleared. The technique employed bypasses the need of re-indexing the array upon termination of any of the instances activity, while preventing the array from growing indefinitely.

CLASS PROPERTIES AND METHODS

controlArray
clearControlmethod

PROTOTYPE METHODS

initCallmethod
setTimeoutmethod
setIntervalmethod
clearTimermethod

A timer can be initialized by any object. Example:

this.quickTip.timer = new pergola.Timer();
this.quickTip.timer.initCall({
  handle: this.quickTip,
  fn: "quickTipShow()",
  delay: 700
});

The handle property
Object. Reference to the object that is initializing the timer instance.

The fn property
String. This is not treated as a User Function. The string represents a function call with the parenthesis to be used by the window.setTimeout() or window.setInterval() methods.

The delay property
Number. Delay in milliseconds for window.setTimeout().

Example of a stepscroll timer for a scroll button:

this.timer = new pergola.Timer();
this.timer.initCall({
  handle: this,
  fn: this.timerString,
  delay: 400,
  frequence: 20
});

The frequence property
Number. The repetition frequence for window.setInterval().

In the first example we assigned the delay property; only window.setTimeout() will be used.

In the second example we assigned the delay property and the frequence property; window.setTimeout() and window.setInterval() will be used.

If we assign the frequence property only, window.setInterval() only will be invoked, with no delay.

A Timer instance is cleared on events by invoking its clearTimer() method:

this.timer.clearTimer();

Note that IE8 has trouble with the window.clearInterval() method if not invoked through an event. To be more precise, IE8 simply does not clear the interval. If you attempt to clear an interval after a variable state for example, with no event, the interval will not be cleared and that may result in a stack overflow or the browser freezing. When you create a Timer instance always invoke its clearTimer() method form a function that is passed an event. The simple progress bar demo, which should not be copied/used, shows a stop button in IE8. A click on the button clears the timer.

ToolBar class
PROTOTYPE PROPERTIES

fill:skin.toolBar.fill
width:0
rx:skin.toolBar.rx
height:skin.toolBar.height
advanceX:0

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
bar<rect> element

Example from the Window build() method:

this.toolBar = new pergola.ToolBar(this.id +"_toolBar");
this.toolBar.Build({
  owner: this,
  parent: this.commands,
  y: this.topBar.height
});

The value of the advanceX property is updated with every tool button that is added to the toolbar.

ToolButton class

This is a subclass of Button. Its only purposes are its typical visual design and the on-and-off switch functionality.

PROTOTYPE PROPERTIES

fill:skin.toolButton.fill
stroke:skin.toolButton.stroke
maskFill:skin.toolButton.maskFill

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

A tool button is structurally equal to a regular button. See the Button class in this chapter.

A window lens tool example:

this.lensTool = new pergola.ToolButton.(this.id +"_lensTool");
this.lensTool.Build({
  owner: this,
  parent: this.zoomGroup,
  fn: "lensToolInit",
  width: barH,
  height: buttonH,
  extra: {
    rx: rx
  },
  symbol: {
    symbol: pergola.symbols.lens,
    x: 12,
    y: 10
  },
  selected: false,
  quickTip: "lensTool"
});

A tool button has one specific property that is intended to provide the button with on-and-off states.

The selected property
Boolean. Normally we would set its value to false for a button in off state initially. Besides the regular click to switch the state, the lens tool and the hand tool also act mutually as radio buttons, selecting one deselects the other.

TopBar class

This is the generic top bar. Pergola windows use the WindowTopBar class.

PROTOTYPE PROPERTIES

height:skin.topBar.height
fill:skin.topBar.fill
textFill:skin.text.fill
extra:{}

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
bar<rect> element
title<text> element

Example from Panel build() method:

this.topBar = new pergola.TopBar(this.id + "_topBar");
this.topBar.Build({
  owner: this,
  parent: this.control
});

ValueInputBox class
PROTOTYPE PROPERTIES

width:skin.valueInputBox.width
height:skin.valueInputBox.height
hasButtons:skin.valueInputBox.hasButtons
realTime:false

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
rect<rect> element
text<text> element

Example:

myValueBox = new pergola.ValueInputBox();
myValueBox.build({
  owner: this,
  parent: g,
  caption: {
    x: -16,
    y: 15,
    'pointer-events': "none",
    textNode: "G"
  },
  max: 255,
  min: 0,
  value: 128,
  target: myObject,
  propName: "g",
  fn: myObject.fn,
  realTime: true
});

The caption property
Object. Optional. This object can have exactly 2 properties: position, which gets "left" (default) or "right"; text, which gets a text object. The property caption mutates to become a reference to the <text> element.

The min and max properties
Number. The minimal and maximal values.

The value property
Number. The initial value.

The realTime property
Boolean. It can be set to true for objects that only require little processing/rendering, like is the case for the ValueInputBox.svg example.

The target, propName, and fn properties
These properties relate to user functions and are covered under the chapter USER DEFINE EVENTS AND FUNCTIONS. Note that propName is specific to this class.

Window class
PROTOTYPE PROPERTIES

isOpen:true
minimized:false
isFull:false
resizable:true
fill:skin.window.fill
x:200
y:160
margin:4
hasCommands:true
hasToolBar:true
hasZoomAndPan:true
scrollType:"regular"

Note: scrollType: "navigation" is not implemented in this version of Pergola. Do not override this property.

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

control<svg> element
frameFrame instance
commands<g> element
topBarTopBar instance
progressProgress instance
toolBarToolBar instance (on option)
menubarMenubar instance (on option)
backgroundBackground instance
childDocChildDoc instance
tabWindowTab instance
previewPreview instance

All other visible components are built depending on the options, for example the command buttons are not built if the property hasCommands is overridden.

Windows always have scrollbars.

Example, the first window in windowsfull demo:

var window1 = new pergola.Window("Window 1");
window1.Build({
  fill: "#FFFFF0",
  width: 640,
  height: 480
});

Note: the fill property applies to the window background.

Example from the windowbasic demo:

var myWindow = new pergola.Window();
myWindow.Build({
  hasCommands: false,
  hasToolBar: false,
  hasZoomAndPan: false
});

Note: the commands group is created even if the window doesn't have command buttons.

Note: if hasZoomAndPan is set to true and hasToolBar is set to false, then the latter is forced to true and the window has a toolbar.

Example, the second window in windowsfull demo:

window2.Build({
  x: 240,
  y: 200,
  contains: ellipses.dum(),
  ...
});

The contains property
Reference to element or function.

There are three possible ways to append contents to a window:

  1. through the Load class
  2. by assigning an appropriate value to the contains property
  3. by manually appending contents

Case 1: this method is fully explained under the Load header in this chapter.

Case 2: the contains property gets a reference to an existing element –probably a container– which may have been defined in some other file, and not necessarily appended anywhere, or a helper function specifically designed for, like is the case for the ellipses.dum() function defined in artwork.es file –there's also an ellipses.smart() there– for the windowsfull demo. The contents are appended at runtime.

Case 3: the element/container will be created and appended on the fly, or perhaps programmatically at some later stage. This alternative method is shown commented at the end of windowsfull.es file. If and when we use this method we need to explicitly call the getBBoxOnMutationEvent() method if we target, or simply care about, ASV (Mutation Events are not implemented in ASV):

if (!pergola.mutationEvnt) {
  myWindow.getBBoxOnMutationEvent(myWindow.childDoc);
});

These three methods offer a wide range of dynamic possibilities for appending or updating window contents.

The component objects of the window are covered under their respective class headers in this chapter.

WindowTab class
PROTOTYPE PROPERTIES

'font-size':skin.windowTab['font-size']
height:skin.taskBar.height - 6
width:skin.windowTab.width
fill:skin.windowTab.fill
maskFill:skin.windowTab.maskFill
maskStroke:skin.windowTab.maskStroke
textFill:skin.text.fill
textFillInverse:skin.text.fillInverse

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
button<rect> element
text<text<> element> element

A window tab is automatically created for each window and appended to pergola.tabsDock.group only if the hasCommands property of the window is true.

Example from the Window build() method:

this.tab = new pergola.WindowTab(this.id +"_tab");
this.tab.Build({
  owner: this,
  parent: pergola.tabsDock.group
});

WindowTab instances are managed as a system component. See also the TabsDock class in this chapter.

WindowTopBar class

This class is a subclass of TopBar.

PROTOTYPE PROPERTIES

height:44
fill:skin.topBar.fill.window
maskFill:pergola.shade([100, 100, 100], 25)
maskOpacity:0

INSTANCE PROPERTIES – SIGNIFICANT ELEMENTS AND COMPONENT OBJECTS

group<g> element
bar<rect> element
mask<rect> element
title<text> element

Example from the Window build() method:

this.topBar = new pergola.WindowTopBar(this.id +"_topBar");
this.topBar.Build({
  owner: this,
  parent: this.commands
});

THE SVG NODE BUILDER

As we've seen in the INTRODUCTION, Pergola does not define classes for SVG elements. Instead it uses the SVG library represented by the implementation itself. For that Pergola uses a revolutionary node builder which is an interface between Pergola and the implementation's SVG library.

The node builder is a method of the pergola object. There exists a shortcut global variable for this method named $C. So the call to the node builder looks like this:

$C();

The method returns the SVG element. In the case above the element is simply created and appended, but if we assign the call to a variable or property, the element is referenced:

var myElement = $C();

The variable myElement references the DOM node of the element.

Just like the Pergola constructors do, you will be likely to use the node builder extensively for any SVG elements you may need to create. The node builder is extremely intuitive and easy to use in that you don't need to learn any special syntax, pseudo-language or any corny ideas. You simply pass exactly 1 parameter of type object.

In this object we define any properties corresponding to SVG attributes that we need to set. We also define 3 properties the names of which are predefined and are not SVG attribute names. These are:

elementStringRequireddesignates the element
appendToReferenceOptionaldesignates a parent node
textNodeStringOptionalused with text element, defines the text node

All the other properties designating SVG attributes names will be named using the attribute name itself, exactly. For properties designating attributes in the SVG namespace which contain illegal characters for JavaScript variable/property name we use the string notation.

EXAMPLES

a group:

$C({
  element: "g", 
  id: "myID"
  transform: "translate(100 100)",
  appendTo: pergola.user
});

a rectangle:

$C({
  element: "rect", 
  width: ..., 
  height: ..., 
  fill: ..., 
  stroke: ...,
  'stroke-width': ...,
  'fill-opacity': ..., 
  appendTo: parentNode
});

Note: Pergola does not carry out any checks for inconsistencies between the element and the attributes you are assigning, or between the attributes and their values. That is left to your responsibility, just like if you were coding SVG.

an image:

$C({
  element: "image", 
  width: ..., 
  height: ..., 
  "xlink:href": URL, 
  preserveAspectRatio: ...,
  appendTo: parentNode
});

You will not need to worry about the xlink namespace, it all happens behind the curtains.

Yes, but what about nodes that have descendants? Well, guess what, we just append the descendants to their parent. Example from the skin file:

var gradient = $C({
  element: "radialGradient", 
  id: "radioButtonGrad", 
  cx: "65%", 
  cy: "65%", 
  r: "100%",  
  fx: "65%", 
  fy: "65%", 
  gradientUnits: "objectBoundingBox", 
  appendTo: defs
});

$C({
  element: "stop", 
  offset: "0%", 
  "stop-color": "#F0F0F0", 
  appendTo: gradient
});
$C({
  element: "stop", 
  offset: "100%", 
  "stop-color": "#A8B0B8", 
  appendTo: gradient
});

We have seen that the <text> element can have one specific pseudo-attribute property named textNode. We just assign a string to that property. A text node will be automatically created and appended to the <text> element:

this.text = $C({
  element: 'text', 
  x: width / 2 - 1, 
  y: textPos, 
  'font-size': this['font-size'], 
  fill: this.textFill, 
  'text-anchor': 'middle', 
  'pointer-events': 'none', 
  textNode: this.title, 
  appendTo: this.group
});

The above code is taken from within a build() method and shows the use of properties and local variables in setting the values. We just keep in mind that while strings containing numeric values can be of type Number, numeric values with unit identifiers must be of type String, as in:

'font-size': '9pt'

As we have already seen, properties designating attributes in the SVG namespace which contain illegal characters for JavaScript variable names will have names of type String and must be referenced using the string syntax instead of the dot syntax, as in:

myRect['fill-opacity']

That's that and there's nothing more to it.

The node builder is known to work with all the elements: container, primitive, text, tspan, gradient, filter, pattern, foreignObject, image, cursor, etc. Should you encounter problems with a particular element or should you have requests for enhancements, please let us know. Pergola's node builder is Open Source and is maintained by the author who is the person to contact for these issues.

THE HTML NODE BUILDER

Pergola also implements an interface for building HTML elements, the primary scope being using it in conjunction with the <foreignObject> element.

The <foreignObject> element was not implemented in ASV, and is not widely and fully supported yet.

Here is an example of real usage taken from the panelWithTabs.svg example. It uses <foreignObject> to build an html table for the first tab of the panel:

tabbed.tab1HTML = function() {
  var fObj = $C({
    element: "foreignObject", 
    width: this.paneWidth,
    height: this.paneHeight,
    appendTo: this.pane
  });
  var body = pergola.createHTMLElement({
    element: "body", 
    appendTo: fObj
  });
  var table = pergola.createHTMLElement({
    element: "table", 
    border: 1, 
    cellpadding: 2, 
    cellspacing: 2, 
    style: "width:100%; background-color:#FFFFF0;", 
    appendTo: body
  });
  for (var i = 0; i < 3; i++) {
    var tr = pergola.createHTMLElement({
      element: "tr", 
      appendTo: table
    });
    for (var j = 0; j < 3; j++) {
      var td = pergola.createHTMLElement({
        element: "td", 
        style: "height:" + ((this.paneHeight - 30) / 3) + "px; text-align:center;", 
        appendTo: tr
      });
      var p = pergola.createHTMLElement({
        element: "p", 
        style: "font-size:16pt; font-weight:bold; color:#F4F4F4;", 
        textNode: ("CELL " + (i * 3 + j)), 
        appendTo: td
      });
    }
  }
}

Unfortunately, as you can see from the demo, the above renders perfectly in Firefox only. The webkit implementations render the table but with no borders or backgound color. Opera doesn't show anything. We will discuss this with the concerned parties and we are confident that things will get straighten up quickly.

Concerning ASV, the <switch> element, used programmatically, doesn't seem to work and show the alternate <text> element. So we can use this as last line in the function:

var string = "This implementation does not support foreignObject";
if (pergola.browser.asv) $C({element:"text", textNode:string, appendTo:this.pane});

Note that contrary to the SVG node builder, where we can use the textNode property only when creating a <text> or <tspan> element, with the HTML node builder we can set it for practically any element, <body>, <div>, <p>, <span>, etc.

Other that that, the same rules exposed in THE SVG NODE BUILDER apply.

USER DEFINE EVENTS AND FUNCTIONS

PASSING A USER FUNCTION TO THE CONSTRUCTOR

All Pergola objects have built-in event listeners and handlers to ensure the proper functioning of complex objects, interactivity between objects, where that applies, and effects. Naturally Pergola allows you to pass your own functions and methods, as well as associated event listeners, in the calls to constructors. As you probably know well by now, the parameter passed to the classes' build() methods is an object. The properties of this object become then instance properties of the new object .

HOW DO I SET THE USER FUNCTION PROPERTIES?

In the object you are passing as parameter you assign the fn property and, optionally, target and ev properties.

It is also possible to set other User properties to be processed in your User Function. Needless to say, in no case such properties will be processed by any method of the library.

target
Reference to object.

ev
String. Event name, such as "mousedown".

fn
Its value can be one of several types:

  1. prototype method
  2. instance method
  3. class method
  4. top level function
  5. function literal

casesvalueexample
A; BString"myFunction"
Creference to class methodmyClass.myFunction
Dreference to functionmyFunction
Efunction literalfunction() {...} or
function(evt) {myFunction(evt, targetObject)}

HOW DO I DECIDE WHAT IS BEST?

While experienced developers will know what fits best their needs depending on the situations, what is important to know is that in all cases the this keyword refers to the object, not to its target object, and the target object properties are accessible through the target property; in all cases the body of the function can be strictly the same. You will generally find instance methods appropriate. It is unlikely that you will need to define a class method, considering that if it relies on the this keyword it will produce unexpected results if invoked directly by other objects unless you use the call method, ex.: myClass.myFunction.call().

Note that using the string to designate the function name allows to reference a function or method that has not yet been defined, which can prove extremely useful for the interactivity with external documents, while the hard-coded name implies that the method/function must have already been defined.

CAN I ADD EVENTS ON ANY OBJECT?

In theory, yes. In practice it is preferable not to add events that could compromise the good functioning of some systemic objects like, for example, command buttons (minimize, full, close), scrollbars, etc. Abstract classes do not treat user events.

PARTICULAR CASES

ValueInputBox class. When creating an instance of this class we need to define one extra property: propName, which is required. Since more than one instance of this type of object may reference the same target, each acting on a particular property of target, we assign to propName the name of a property of target, as a String. A tipical example is when we are using three value boxes to manipulate the r,g and b components of the color of some object. For this class we do not define the User Event property ev.

Slider class. Because of the particular behavior of sliders, an instance of this class does not allow User Events. You can assign the target and the fn properties only. The user function is invoked on the "mousedown" event. The reason for this being that the dragging area is the top layer and once activated by the "mousedown" event, any underlying object will not be able to register other user interface events. The User Function will be invoked on the built-in "mousedown" event.

DialogButton instances are configured to be handled by their owner object, typically a dialog panel, if the owner property has been set, but they can also register user functions. If the owner object does not have an event handler and no user function has been assigned to the button, then nothing will happen. Panel, which is the class that manages dialogs, combo-boxes and other layouts, has an event handler. If you place a dialog button somewhere else it is not guaranteed that the owner object to which it belongs has an event handler. Check the property sheets to see which classes have prototype event handlers.

The classes Menu, MenuItem, ListItem, have mouse events management fully preconfigured and expect user functions, without which they are pretty meaningless. Apart from the handling proper to its internal logic, its list items (MenuItem) have "mousedown" and "mouseup" listeners but no associated method/function. The mouseup event triggers the check and call of the user function, if set, after managing its basic business. The mechanism for activating items with the active property initially set to false, is not provided, as it is application dependent.

OBJECT EXTENSIONS

Pergola extends the JavaScript built-in classes Object, Number and String with some methods and properties.

Object:

Number:

String:

SKINS

The Pergola Skin has the primary role of determining the appearance of your application. It is designed to work in a manner similar to that of a CSS file, while adding some notable features:

A skin is a property of the pergola.skins object found in the skins.es file. This object has 1 property named rubber, which is the default skin. The rubber property is a function. A new skin would then be a new property of pergola.skins, named perhaps galaxy. The galaxy function will be structurally an exact copy of rubber.

The two major roles of this function are:

  1. Define the pergola.presentationAttributes object
  2. Build the gradients and patterns

Each of the property names defined in pergola.presentationAttributes is a direct reference to one of the Pergola classes: button → Button, with the exception of two properties: all, which defines some common defaults for all classes, and text, which is used by several classes.

These properties are objects and their properties designate primarily SVG painting and/or geometrical attributes, but also others. Whenever possible their names are exact replicas of SVG attribute names. Whenever this is not possible for conflicting reasons, Pergola uses pseudo-attribute names, for example a class can have a fill and a maskFill property.

The values of all the properties here are editable of course, but properties cannot be added or removed. An addition would be simply ignored, while a removal will result in a runtime error or bad rendering.

So this is the place where to set the default appearance of your objects. No need to hack the classes' prototypes. In any case the rendering properties do not get hard values there; they simply reference their equivalents in the pergola.presentationAttributes object.

It is not recommended to edit the original rubber skin. Instead, make a new skin by copying rubber, give it a new name and edit the new skin. You will then have two skins, one of which will be always known for working properly. More on this in the MAKING A NEW SKIN section.

Worth of note, for those of us who are not interested in making new skins it is still worthwhile taking a close look at the engine for the automatic production of shades, the pergola.shade() method, as it can be very useful in several situations like the calculation of the shades for geographical altitudes in a legend, the progressive tone changes in a bar chart, an application for architects for calculating coordinate colors, etc.

You will be able to assign the skin for a particular project in the configuration file that must accompany each of your projects. See the CONFIGURATION chapter.

HOW DOES THE SKIN WORK?

A skin needs a theme color to work with. We set the theme color in the configuration file as above. The skin then has a color lab, which is the class method pergola.shade() we just saw, that you can use for setting shades for your gradients, fills, strokes etc. Its use is explained in the next section.

WHAT FORMAT CAN I USE TO SPECIFY MY THEME COLOR?

The type of the theme property is a string representing an RGB value in any of the following formats:

RGB value in hexadecimal notation:

"#CD5C5C"

RGB value in functional notation (integer values or percentage values):

"rgb(205, 92, 92)" or "rgb(80%, 36%, 36%)"

Recognized color keywords:

"indianred"

User define color name (as defined in the pergola.customColorNames object).

"nürburgring"

Note: the pergola.customColorNames object is located in the skins file at the bottom.

MAKING A NEW SKIN

WHAT DO I NEED TO KNOW FIRST?

While developing a new skin or simply setting a new theme it is important to keep in mind the relation between the skin and the theme.

Essentially, the skin is the look and feel. A skin can also suggest a texture. Although SVG filters offer great capabilities for visual effects, they haven't been used here due to the notorious rendering speed limitations. The “rubber” theme effects are largely based on gradients and, in one case, patterns (the Pirelli metro tiles of the scroll bar slider).

A theme is a base color. The colors of relevant objects are shades of the base color, although they can be unrelated as for example when we need neutrals (black, white, grays), which we know will work with most base colors. These shades are applied to fills, strokes and gradients and are computed by the skin engine.

We set the skin and theme of a project in its config file. They are properties of the pergola.settings object and their value is a string:

skin: "rubber"
theme: "nürburgring"

See the SKINS chapter for the recognized color formats for the theme value.

The skin is generated by the skin function, which is a property of the pergola.skins object.

To make Pergola use a new skin that we have perhaps just made and that we named galaxy, we set the skin property of pergola.settings to “galaxy”:

skin: "galaxy"

That's all. Our application will run with the galaxy skin.

Consider the base skin “rubber”, it has a soft touch feel which is suggested by its weak contrasts. Although technically it works with any color scheme determined by the theme color, the best definition is obtained with theme colors that are in the same luminance region of the base theme color (the medium-high register in the case of the “rubber” skin). In practice, the range of usable themes for the rubber skin goes from the medium dark, like "darkslategray" rgb(47, 79, 79) for example, to the very light, like "gainsboro" rgb(220, 220, 220) or even "papayawhip" rgb(255, 239, 213). Outside of this range the rubber skin may not have enough contrasts, although, objectively, it does work with black and near white as well. After that, it's a matter of personal taste.

As a general rule, base colors with a very high contrast between its components and extremely light or dark colors will generally produce inappropriate or unpleasant contrasts, no matter how painstaking your attempts to refine them.

HOW DO I CHANGE THE THEME?

As we saw, the properties of the pergola.skins object are functions. This object doesn't have any other properties. The first statement of a skin function is the theme color:

pergola.theme = pergola.settings.theme;

Of course for testing purposes we don't need to change the theme in the config file every time, we can bypass that value by setting right here any color we want to try out.

pergola.theme = "#B8CAD2";

Let's change that to navajowhite:

pergola.theme = "navajowhite";

Done. See the SKINS chapter for the recognized color formats.

I'M ALL SET, I WANT TO MAKE A NEW SKIN.

OK, we want to make a new skin named galaxy. What we suggest as best practice is to proceed like this:

  1. Copy the rubber skin function, which is a property of pergola.skins, complete with its gradients definitions.
  2. Paste below as a new property of pergola.skins and name it galaxy.
  3. Set the color of pergola.theme property to the base color you have in mind for galaxy.
  4. In pergola.settings (config file) set the skin property to: "galaxy".
  5. Proceed with the modifications of the new skin.

YES, BUT WHAT ARE THOSE VALUES PASSED TO pergola.shade()?

That's a very good question. The fill colors and the stop colors need to work with many color themes and for this reason they cannot be hard coded, of course. Instead, each color component (r, g, b) is expressed as the percentage of its corresponding component of the theme color. They are not absolute rgb percentages! The values are passed as an array. To see how they are defined let's work for example on the toolbar gradient with id "toolbarGrad":

  1. Start off by testing with hard coded values until you find those which look terrific with the current theme color. You may want to add or delete stops and tweak the offsets until you are satisfied.
  2. For every color that you've set, calculate the percentage of each of its three components in respect to its corresponding component of the theme color. Pass those percentages in the order of magnitude found in the theme color. For example, if the dominant in the theme color is blue, then green, then red, we pass the blue percentage as first parameter, then the green percentage, and last, the red percentage. DO NOT pass them in the rgb natural order unless of course that happens to be the dominance sequence in the theme color. When dealing with neutrals the order doesn't matter because the engine equalizes them. This means that it doesn't make much sense to experiment right off with a neutral theme (black, gray, white), unless you definitely will not care for other theme colors for the skin you are creating. The equalization is necessary to avoid unwanted pinkish or greenish or blueish grays in neutral themes.
When you get acquainted you can skip the first phase.

Finally, test your skin with different theme colors, bearing in mind that a skin need not necessarily work with a great number of theme colors. It would be hopeless to think for example that a chrome skin would work well with an “indianred” theme. But sometimes the results can be surprising in respect to expectations.

WHY THE CALL TO pergola.shade() HAS SOMETIMES TWO PARAMETERS?

The second parameter is optional, and can be extremely useful. In fact, whenever you need a shade closely related to the base color it is much simpler and faster to use it, rather than the method above.

This parameter can have positive or negative values. A positive value produces a lighter deviation of the color and a negative value, a darker deviation. The values of the components' percentages parameter (the array) are simply the same as the adjacent stop, and more generally just the base color ([100, 100, 100]). The algorithm used acts on the luminance of the shade. For positive values it takes several factors into account to yield a credible result across a wide range of tones, by altering the luminance and the saturation. For negative values it simply darkens the tone. Negative values normally need much smaller figures than positive values. Common uses of the second parameter are typically to highlight or shadow the edges of some objects (like the button gradients), or more generally to get the variations for a one color gradient.

Another useful effect is to use it as a de-saturator, by passing lower components percentages with a positive value for the second parameter.

You will see that its use is very intuitive and fun. In simple words, the second parameter is a helper to avoid the long and fastidious trial and error definition of shades.

SYMBOLS LIBRARY

A Pergola symbol is not an SVG <symbol> element.

Symbols are properties of the pergola.symbols object, but can also be .jpg or .png images. They are defined in the symbols.es file. A symbol definition is an object with as many properties as needed, each defining primitive elements and their attributes. The property is assigned an object, the properties of which comply with the specification of the Node Builder –any SVG attribute can be declared as property here, as long as the attribute is coherent with the primitive. Example:

lens: {
  e1: {
    element:"path", 
    d:"M4.5 3.4l6.5,5", 
    stroke:"#323232", 
    "stroke-width":1.25, 
    "stroke-linecap":"round"
  },
  e2: {
    element:"circle", 
    r:5.75, 
    fill:"#FFFFFF", 
    stroke:"#727272", 
    "stroke-width":1.5, 
    "fill-opacity":.6
  }
}

The property names (e1, e2, ...) are arbitrary, but when defining new symbols perhaps it's better to stick with this convention for consistency. e stands for element.

Note: although the definition of a Pergola symbol is similar in structure to that of the SVG <symbol> element, Pergola does not create a physical symbol node in the defs or anywhere, and instances of it are not created by the <use> element.

A symbol instance is created by invoking the pergola.Class.symbol() method using the call() method of the Function object. Example:

this.symbol = pergola.Class.symbol.call(this, this.symbol);

Invoking this method can be considered equivalent to using the SVG <use> element. The method returns a <group> containing the symbol's elements.

In practice we never have to invoke the method directly when we need to use a symbol with some object. We first define a symbol property in the call to the build() method of the object's class. Example, a symbol used by a button:

var button5 = new pergola.Button();
button5.Build({
  ...,
  symbol: {
    symbol: pergola.symbols.arrow.large.down,
    x: "center",
    y: "center",
    scale: ".65 1.5",
    fill: "#80C8FF"
  },
  ...
});

We have there a symbol object which is a property of button5. The object itself has a symbol property which can be considered to have the same role of the <use> element (we didn't name it “use” to avoid any equivocal relation with the <use> element).

The symbol property
Reference to object or String. We can assign to this property the reference to one of the pergola.symbols defined in the symbols.es file or the URL of some image, as in the customToolButton1 example of the toolbuttons demo:

symbol: {
  symbol: pergola.path + "rsc/symbols/spectrum.png",
  width: 16,
  height: 16,
  x: 6.5,
  y: 4
}

in which case we also declare the width and height properties as attributes for the <image>.

The x and y properties
Number or String. If set, these are transferred to the additional transformation translate(x, y), after any scale transformation. Besides the legal values, these properties can get the value "center" which gives a satisfactory result only for symbols that have the bounding box origin at 0, 0 or at least close to that, which is the case for most of Pergola predefined symbols.

The scale property
String. The scaleX and [scaleY] with no parenthesis (). If set, the scaling is applied to the individual elements, not to the group.

The fill property
String. If set the fill is applied to each individual element constituting the symbol.

The opacity property
Number or String. If set the opacity is applied to the group.

All the symbols used in the demos fall under the same scenario of a class instance using a symbol, but symbols can also be created as a standalone object, for example for creating a desktop icon or a logo. Suppose we want to make an icon; we follow these steps:

  1. Create an object
  2. Define a group property referencing a <group> element
  3. Define a symbol property referencing an existing symbol
  4. Invoke the method

Example:

myIcon = {
  group = $C({
    element:"g", 
    id:"myIcon", 
    transform:"translate(20 40)", 
    appendTo:pergola.user
  }),
  symbol: pergola.symbols.myCuteIcon
};

pergola.Class.symbol.call(myIcon, myIcon.symbol);

From the step 2 above we can easily understand that it is a limitation, i.e. symbols can only be appended to a <group> container designated by the group property of some object. Why not use the parent property so commonly used by the classes' build() methods? Because the facility of defining the symbol object directly in the call to a build() method excludes the possibility to designate a parent group that doesn't exist yet.

SHAPES LIBRARY

A shape is an object defining a primitive and its geometrical attributes. No rendering or any other attributes are defined there. The shape objects are properties of pergola.shapes. The shape library is the shapes.es file.

The triangle shape:

triangle: {
  element: "path",
  geometry: {
    d: "M0,31.5h32l-16-27.712z"
  }
}

The object has exactly 2 properties:

element
String. The name of an SVG primitive.

geometry
Object. Its properties are the element's geometrical attributes: d for path; cx, cy, rx, ry for ellipse; points for polygon; etc.

Note: contrary to the symbol, which can be made of several elements where their name and attributes are defined in one single object for each element, and then passed as is to the Node Builder, the shape object is processed and the rendering attributes are added depending on the context. The property name geometry makes it very clear that no rendering or other attributes must be added.

This version of Pergola only implements shapes for the Button class. It's build() method applies the rendering attributes defined in the prototype and, eventually, other attributes and transformations found in the extra object.

We use a shape by assigning the shape property in the call with the reference to a shape object from the library:

var shapeButton2 = new pergola.Button();
shapeButton2.Build({
  ...,
  shape: pergola.shapes.triangle,
  ...,
});

As we have seen under the Button header in the CLASSES chapter a shape can also be defined on the fly using the same format of the library shapes:

shape: {
  element: "path",
  geometry: {
    d: "..."
  }
}

in which case the shape will be one of a kind and not reusable.

FILTERS LIBRARY

The filters are located in rsc/filters/filters.es. They are defined using the node builder (appended to pergola.defs which references the <defs id="pergola_defs"> element).

The drop shadow 1:

var filter = $C({element:'filter', id:'dropShadow', x1:'0%', y1:'0%', filterUnits:'objectBoundingBox', appendTo:defs});
$C({element:"feGaussianBlur", 'in':"SourceAlpha", stdDeviation:"2", result:"blurredAlpha", appendTo:filter});
$C({element:"feOffset", 'in':"blurredAlpha", dx:3.2, dy:3.2, result:"offsetBlurredAlpha", appendTo:filter});
$C({element:"feFlood", result:"flooded", 'flood-color':"#000000", 'flood-opacity':0.4, appendTo:filter});
$C({element:"feComposite", 'in':"flooded", operator:'in', in2:'offsetBlurredAlpha', result:"coloredShadow", appendTo:filter});
$C({element:"feComposite", 'in':"SourceGraphic", operator:'over', in2:'coloredShadow', appendTo:filter});

In the folder you will also find some .svg examples of feTurbulence textures to let you preview the filter. The filename corresponds to the filter's ID in the filter's file.

CURSORS LIBRARY

Cursors may be fully, partially or not at all implemented in the existing SVG implementations. In this version of Pergola, cursors are bitmap images only and are located in the cursors folder. This format has been found to have the highest level of portability at writing time.

A cursor is created through a direct call to the Node Builder. Example:

$C({
  element : "cursor", 
  id : "myCursor", 
  "xlink:href" : URL, 
  x : x hotspot, 
  y : y hotspot,
  appendTo : pergola.defs
});

The cursor can then be referenced by its id:

element.setAttributeNS(null, "cursor", "url(#myCursor)");

System cursors are referenced by their CSS names (“move”, “default”, “se-resize”, etc.).

KNOWN ISSUES

  1. Internet Explorer 8 + Vista DEP memory protection
  2. SMIL
  3. External reference in downloaded documents
  4. Local documents download
  5. Events

1. INTERNET EXPLORER 8 + VISTA DEP MEMORY PROTECTION

Action
Removing nodes from the document.

Effect
When refreshing the browser DEP (Data Execution Prevention) prevents the page refresh and shows this notice:

Internet Explorer has closed this webpage to help protect your computer
A malfunctioning or malicious add-on has caused Internet Explorer to close this webpage.
What you can do:
...
Try to return to the page you were viewing
...

When that option is selected the page is rendered normally, the scripts are executed and subsequent refreshes of the page function normally, showing clearly that in reality no memory checks were executed. DEP is simply blocking the add-on, just in case, and leaving the responsibility to the user. If the memory usage was found to be inadequate, DEP should definitely prevent the script from being executed.

Causes
There are several situations that can awaken DEP "suspicions". One that was encountered often is when an object has a property which references an instance of another class, and the elements of which are appended to some other group, and this group is itself an element of an instance of yet another class, such instance being created by the constructor of the original object but made be referenced by a property of another object (Help!). Also found if using the removeChild() method on such elements (setting the property to null after removal of the node doesn't help). Instead, direct operations on a node's children using node properties are not affected. In simple words DEP is not very smart concerning true OOP, but luckily it only gives a warning.

Workarounds

  1. Use a different procedure.
  2. Ignore, since the good functioning of the document is not affected.

2. SMIL

SMIL animation in downloaded documents does not resolve in: Chrome, some Safari builds (?), Firefox.

Workarounds
No known workaround.

3. EXTERNAL REFERENCE IN DOWNLOADED DOCUMENTS

Action
Example: a map referencing a relief image.

Effect
The relative URL of the image will not be valid once the map SVG fragment has been integrated into the document. Affects: Chrome, Opera, Firefox.

Workarounds
Set the URL relative to the SVG file which loads the map, or use absolute URL.

4. LOCAL DOCUMENTS DOWNLOAD (CHROME)

CHROME FAILS TO LOAD LOCAL DOCUMENTS IN BUILD 5.0.375.99

Workarounds
No known workaround.

5. EVENTS

EVENTS DISPATCHING IN FIREFOX

Effect
Dysfunction in releasing the Dragarea after dragging objects. This was also true for Opera prior to version 10.60 Build 3445.

Workarounds
No known workaround.

ONRESIZE EVENT IN FIREFOX

Effect
The onresize event registered on the outmost <svg> element does not trigger the event handler. This will not affect the background and taskbar whose width properties are set at "100%", but does affect windows in maximized state. In that state the dimensions are computed taking into account several parameters–their width is not then "100%". On resizing the browser their full width dimensions will not update.

Workarounds
No known workaround.

MOUSEOVER IN INTERNET EXPLORER – EVENT REGISTERED ON ELEMENT WITH THE DISPLAY ATTRIBUTE SET TO “NONE”

Effect
Popup list: when the display is set to “block” by clicking on the selector, the element of the list that is under the mouse does not trigger the mouseover event.

Workarounds
Possible workarounds were not applied.