PERGOLA LOGO

Tutorial

  1. Introduction
  2. Configuration
  3. Instantiation, Objects And Properties
  4. Classes
  5. DOM Helper
    1. SVG
    2. HTML
  6. User Events And Functions
  7. Timer Class
  8. Undo Class
  9. Key Class
  10. Libraries
    1. Filters
    2. Patterns
    3. Markers
    4. Symbols
    5. Shapes
    6. Cursors
    7. Quick Tips
    8. Messages
    9. Skins
  11. How To’s
    1. Making A New Skin
    2. Loading SVG Documents
    3. Adding Window Tools
    4. Creating A System Menu
    5. Custom Context Menu
    6. The Find Dialog
    7. The File Dialog
    8. Adding A Logo Icon
    9. Maps
    10. DataGrid
    11. Using The Debugger
  12. Known Issues

INTRODUCTION

Description

Pergola is a JavaScript SVG native framework for building web applications, user interfaces, mapping applications, presentations, and web sites. Its Object Oriented Development model is designed to be used internally by its classes, and by the developer, in a consistent manner throughout all the development phases.

Core components and features:

Architecture

The architectural scheme of Pergola is based on class inheritance and prototype extensions of the subclasses, and it's built around the placeholder object pergola, establishing a namespace, a superclass and subclasses. Classes are bound to the pergola namespace, and most of them inherit from the Class superclass, which defines prototype methods and common properties. Each class extends its prototype with specific properties and methods.

Several services and system components use lazy loading. Therefore, a small application that doesn't use them will not have to carry the overhead weight of the system. You will also see that hevy-duty applications do not suffer from the overhead weight of the system, and that their performance and usability is comparable or slightly inferior to desktop applications, or even superior in some cases.

Pergola does not define any pseudo-language or proprietary representation of the DOM and its methods. Instead it exposes the SVG library (the browser's implementation) directly thanks to its progressive DOM Helper which is at the core of all the building blocks of the classes as well as the user's objects.

Pergola also implements a feature for some of its classes that allows the user to define, very simply in the call to the constructors, any SVG attributes as instance properties, using SVG vocabulary and 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 DOM output as it would be if written manually by a competent SVG coder.

Pergola runs equally well in a standalone SVG context or in HTML and is fully compatible with all the major SVG implementations: Firefox, Opera, IE (Trident), Safari, Chrome, ASV (definitively dropped since version 1.3.8).

CONFIGURATION

A configuration file is bound to each project where a few environment variables are set. A config.js 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.js file at the root level in the example's folder. Note: after the initialization of Pergola these variables are no longer used and can be reassigned.

The variables in the config.js file:

container

Its value is 0 for standalone SVG context, or ID (string) of the HTML element hosting Pergola, for example container="mySVG".

If an HTML container is specified, it is referenced by the pergola.container property; Pergola then creates the main <svg> element, referenced by the pergola.doc property, and appends it to pergola.container.

If an HTML container is not specified pergola.doc references document.documentElement, the original SVG document where Pergola is loaded. Note that in this context pergola.container is undefined.

svgWidth

Leave commented for standalone SVG context. When running Pergola in a HTML-SVG context it must be set to the appropriate value if svg goes in an iframe or div with overflow: auto. Default is 100%.

svgHeight

Leave commented for standalone SVG context. When running Pergola in a HTML-SVG context it must be set to the appropriate value if svg goes in an iframe or div with overflow: auto. Default is 100%.

path

The path to the “pergola” folder relative to the arborescence of your project. For each of the demos this is set to "../../pergola/".

skin

Designates the skin to be used by the project. Pergola currently has two skin definitions: rubber and office. They are properties of the pergola.skins object.

theme

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

undos

The number of undo levels.

systemMenu

Determines whether pergola has a system menu on the taskbar; if true, a Menubar object is automatically created and assigned to the pergola.menubar property. Learn how to create a system menu.

taskbarPosition

A string specifying the system taskbar position. The allowed values are "top" and "bottom".

If set, it allows you to place a logo on the taskbar. Learn how to place a logo icon on the taskbar.

decimals

The number of decimals.

debug

Specifies if the pergola.debugger has to be used. Values: false, true, or number of lines (default is 20). It should be set to false when releasing your project.

INSTANTIATION, OBJECTS AND PROPERTIES

This chapter is an overview of Pergola's Object Oriented Development model.

All Pergola classes are in the pergola namespace, and are broadly divided in two categories, those (the majority) that build interface controls and other graphical objects, and those that provide services.

Classes from the second category are: pergola.Timer; pergola.Key (keyboard input); pergola.Undo (undo/redo); pergola.Layout; pergola.RubberBox; pergola.Dragarea; pergola.ContextMenu.

All constructors have a prototype method named build (except for pergola.Undo and pergola.Key, whose initializer methods are named register, and pergola.Timer, where it is named initialize), allowing both method chaining and deferred construction. The latter allows predefining objects and their component objects, keeping a clean structure that gives at a glance control, and allows dynamic or conditional construction, together with the capability of self-referencing and of accessing the inherited properties and methods. Instantiation is therefore dissociated from the actual construction.

For a case study let's use the examples in the Examples/Buttons/ folder.

or
button
Fig. 1 – Basic button.

The constructor, or more precisely the superclass constructor, expects one optional string argument that is assigned to the name property, useful for visible names and for debugging, and from which an XML ID is derived and assigned to the id property. If no name is passed, a unique XML name is generated:

The build() method expects an object literal parameter, referred to throughout the Tutorial as initialization object, where you can specify instance properties and methods, as well as prototype properties and methods to override. For most of the constructors the argument is not required (if you skip it the instance will have the default appearance and behavior, as defined in the prototype). In those cases where the initialization object is required you will be notified in its absence.

Properties

Classes designed to produce interface controls and other graphical constructions extend their prototype with properties that define the default rendering. It's important to know that Pergola uses SVG vocabulary 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 let's take an example:

The pergola.Button class produces a compound object made of two elements of the same type (a <rect> by default), 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. The fill property applies for button, and the maskFill property applies for mask.

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

  myButton.stroke
  myButton["fill-opacity"]

All the relevant elements constituting the concrete object are referenced. This allows you to manipulate them without having to use the getElementById() method, or other selection methods. Example:

The container property is common to all classes that produce visuals; it references the object's outmost container, typically a <g> element, or an <svg> element in some cases.

Back to our button example, let's take a look at the initialization object for the build() method. We have seen that, reduced to its simplest expression, the call can be:

The button will have its default appearance and behavior and will be appended to the user layer at position 0,0 (initial values for x and y properties inherited from the pergola.Class superclass).

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

and let's review the properties:

parent : by specifying this property it means that you are overriding the default parent node, as defined by the superclass prototype. The node must already exist in the DOM tree, or can be created inline. In the buttons demo a <g> element is first created and all the buttons are appended to it:

quickTip : this property can get three different formats, a string, an array or a reference to an existing quick tip object. See the pergola.QuickTip class and the Quick Tips library chapter for options.

ev : one (string) or more (array of strings) event types. The specified event(s) 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 event, it may be processed before or after the one registered by default. Learn more in the User Events and Functions section.

fn : the callback function associated with the user events ev, or invoked on an appropriate event registered by default if ev is not specified. Learn more in the User Events and Functions section.

target : example of user defined instance property. You can define any number of arbitrary instance properties. These properties are not processed by the class's handleEvent() method. In the buttons example this property is processed in the fn user function to highlight items in the legend.

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

button
Fig. 2 – Basic button rotated, with symbol (not rotated).

extra : Aha! An object where we can define any property using SVG vocabulary and grammar! The properties you specify in the extra object are not processed (actually, some are tested and processed for fine positioning of objects in order to achieve correct rendering), they are simply integrated with the default collection of properties defined in the prototype. The object passed to the DOM Helper function contains the attributes specified in extra. This means that it is your responsibility to see that those attributes are coherent with the element. 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 extra property. Where this feature is implemented, it's specified in the class's page of the API reference (classes). However, this technique that consists in specifying arbitrary attributes in the SVG vocabulary is widely used. For example, when instantiating classes that are designed to process the text object property, you can declare any number of <text> element's attributes. This is also true for several other properties of type object, where you can define transformations, coordinates, paint attributes, etc. always using strictly SVG vocabulary. 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.

symbol : full explanation of this object's properties in the chapter SYMBOLS Library.

Looking further in the buttons examples we find:

This button overrides the properties parent, y, width and height.

hasTextEffect : determines whether the text has the video inverse animation effect.

text : this property is an object where you can set any <text> attribute using SVG vocabulary, as we have already seen for the extra property.

The textNode string property is treated in the DOM Helper section.

Note: the text property is reassigned to reference the produced <text> element, which can be manipulated. For example a button could say “TALK” in its first state and say “SILENCE” once clicked:

but also by invoking the superclass prototype method textDataUpdate():

The monaLisa button example shows how to embed an image:

button
Fig. 3 – monaLisa button.

symbol : the symbol property of this object can also get an image, in which case the width, and height properties are required. If the x and y properties are not specified the symbol is automatically centered. Note however that this is okay only if the symbol's artwork bounding box origin is at at 0,0–or at least close to that– which is the case for most pergola.symbols (and always the case for images). The symbol is laid over the element referenced by the button's mask property (fig. 3 above). Learn more in the SYMBOLS Library chapter.

button
Fig. 4 – monaLisa button hovered.

image : this object property is similar to the symbol property, and they can be used together. The image is laid over the element referenced by the button's button property (fig. 4 above). For the image object the "xlink:href", width, and height properties are required.

Where Do I Find The Initial Values For The Default Set Of Properties?

The properties are defined in the prototype extension of each class. But that is not where their values are actually defined! Instead they are simply references to properties set in the skin. This allows you to edit them, if you need, without having to hack the classes.

Before anything, you should read carefully the first section of the SKINS chapter. The skin is a function that defines the 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 function also defines all gradients and patterns.

The properties of these objects are mainly paint 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; the former is a system component and the latter is a user control).

Adding a property will not have any effect. Removing a property will most certainly result in a runtime error.

The best way to proceed is of course the one suggested in the SKINS and MAKING A NEW SKIN chapters. This shows a huge advantage over a classic CSS file, you can have more than one set of definitions in the same file! Your different projects can then point to the same skin file and for each project you will then set the name of the desired skin in the config file that is bound to it. Another advantage is that the properties take expressions, of course.

CLASSES

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

The pergola object defines constants, references to system components, libraries, a collection of geometry functions, a collection of color functions, the DOM functions, class operations functions, node manipulation functions, loading external resources functions, the Undo class, the Timer class, the Key class, miscellaneous functions, the Class superclass, the classes, and builds the layers structure in the DOM tree. The overhead weight of the system built by Pergola is light, thanks also to lazy loading applied to some components, and a well written application will be fast and reactive.

The Class superclass is the class from which most classes inherit. Common prototype methods that are used, or likely to be used, by more than one class are defined in the pergola.Class.prototype, where are also defined properties with initial values for some common attributes (x, y, "font-family", "font-size", and stroke), as well as the default parent node.

The classes are specialized constructors and each define their own prototype extensions, besides the inherited prototype. Prototype inheritance is true JavaScript prototype inheritance model, with no referencing or duplication (simple or recursive) or other alien technique.

Organization of the layers in the 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_preemptive" pergola.preemptive
<g> "pergola_notification" pergola.notification
<g> "pergola_quicktips" pergola.quicktips
<g> "pergola_rubberBox" pergola.rubberBox
<text> "pergola_coordinates" pergola.coordinates
<rect> "pergola_dragarea" pergola.dragarea
<g> "pergola_taskbar" pergola.taskbar
<g> "pergola_tabsDock" pergola.tabsDock
<g> "pergola_contextMenus" pergola.contextMenus

Before ending this chapter, there is one special instance property introduced by Pergola that mimics the role of the DOM's parentNode property:

owner : a reference to an existing object (including window), but typically the object that required this instance as a component. For those classes where it is required you get an alert if it's missing. This property allows upstream identification of objects with a higher hierarchical rank, in a manner similar to the DOM's parentNode property, without having to use expensive JavaScript object scan methods, and has a manifold usage, including that of providing access to the owner object's geometrical and other relevant properties during the construction phase of an instance, for example, or that of facilitating dynamic interactivity; we may not know, programmatically speaking, the owner object of some object, but the object does:

The owner property always designates an object, not a class or superclass. To get information about those we can query these properties:

See the list of classes in the API Reference.

DOM HELPER

SVG DOM Node Builder

As we've seen in the introduction, Pergola does not define proprietary classes for SVG elements. Instead it uses the SVG library represented by the implementation itself. The pergola.SVG() function interfaces the implementation's SVG library.

The function returns the element. If you reference the call:

myElement is then a reference to the newly created element.

Just like the Pergola constructors do, you will be likely to use the DOM node builder extensively for any SVG elements you may need to create. The DOM node builder is extremely intuitive and easy to use in that you don't need to learn an intermediate proprietary representation such as a special syntax or pseudo-language. You simply pass one object where you define any properties corresponding to SVG attributes that you need to set, using SVG vocabulary and grammar. The rationale of this concept is explained in the SVG magazine DOM Helper article.

object defines:

The element string property is required and must be the first property specified. The textNode string property is optional and can be set for text and tspan elements. The appendTo node property is optional; appending can be deferred using the appropriate DOM methods.

All other property names will correspond strictly to the element's attributes, and attribute names containing illegal characters for JavaScript variable names must use the string notation, for example "font-family", or "xlink:href". The style and class attributes can also be set. To call this function you can use the shortcut $C.

Examples

A group:

A rectangle:

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:

When building elements that have descendants you create the element first and then its descendants. Example of a gradient as defined in the skin.js file:

We have seen that the <text> element can have the specific textNode property that takes a string. A text node will be automatically created and appended to the <text> element. A contextual example:

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

The DOM node builder is Open Source. A portable version can be downloaded here.

HTML Node Builder

The SVG node builder has two variants:

Example code from the panelWithTabs.html showing the use of pergola.HTML_FO():

The <foreignObject> element was not implemented in ASV, and is not implemented in IE Trident.

Note that contrary to the pergola.SVG() node builder, where you can use the textNode property only when creating <text> or <tspan> elements, with the pergola.HTML() and pergola.HTML_FO() node builders you can set it for all elements that have a text node, <body>, <div>, <p>, <span>, etc.

USER EVENTS AND FUNCTIONS

Defining User Events And Callback Functions For Your Objects

Most Pergola objects have built-in event listeners and handlers that ensure systemic functioning, interactivity between objects (where that applies), and effects. Naturally Pergola allows you to set event listeners and associated handlers for all low level component objects. As you probably know well by now, the parameter passed to the classes' build() methods is an initialization object. The properties of this object become then instance properties of the new object.

How Do I Set The User Function Property?

In the initialization object you define the ev and fn properties. If ev is specified, fn must be specified. If ev is not specified and fn is specified, fn will be invoked on appropriate built-in event type.

ev : event type string or array of event type strings.

fn : its value can be one of several types:

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

casesvalueexample
AString"myFunction"
Breference to class methodmyClass.myFunction
Creference to functionmyFunction
Dfunction literalfunction() {...} or
function(evt) {myFunction(evt[, params])}

All the event types defined for an object are registered with the same callback function.

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. 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 of the Function class, example: myClass.myFunction.call().

Note that using the string to designate the function name allows to reference a 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 must have already been defined. However, an undefined method name can always be used like this:

fn : function() {return this.myMethod()}

Can I Define Events And Functions For Any Object?

Pergola allows to define user events only for objects where it makes sense. In all other cases the user event is ignored. Defining callback functions is more frequent. Each class page in classes specifies whether the ev and/or the fn properties can be set.

TIMER CLASS

The pergola.Timer class API is fully documented in the SVG Magazine Timer Class article. This class does not inherit from the pergola.Class superclass.

To create a pergola.Timer instance you pass an object to its initialize() method, where the property callback is required, and at least one of the properties delay or frequence is required. Note that the structure of the Timer class allows instantiation with or without the new operator; it will not make any difference. The object can have any number of properties. All properties defined in the object are inherited by the Timer instance.

A Timer instance can use the window.setTimeout() and window.setInterval() methods independently or combined, depending on the presence of the properties delay and frequence in the call to the initialize method.

Instantiation:

The callback function property is required. The timer instance is passed as parameter to the callback function.

If the handle object property is specified the callback function is invoked as a method of handle. You can assign any object to this property, not necessarily that through which you are instantiating the Timer. If the handle is not specified the callback function is invoked as a method of the timer instance, the this value and the variable name designating the parameter (timer instance) in the callback function, are then synonyms.

delay / frequence cases:

  1. delay only is specified: setTimeout() is used.
  2. delay and frequence are specified: setTimeout() and setInterval() are used.
  3. frequence only is specified: setInterval() is used.

If the destroy string is specified, it designates the variable or property to which you are assigning the Timer instance. In this case only, the instance will be destroyed upon termination of the timed process.

A Timer instance is cleared by invoking its clear() method:

This does not destroy the Timer instance, which you can then reuse by invoking the pergola.Timer.initialize() method with no parameter object for relaunching an animation or a timed process with the same properties. Or you can override any property, including the callback function. The SVG Magazine Timer Class article shows several examples of instantiation, callback functions and recycling, explained in detail.

UNDO CLASS

The pergola.Undo class does not inherit from the pergola.Class superclass.

To use the undo/redo feature an object must first define a pergola.Undo instance and register it, for example:

The undoManager property name is arbitrary, note however that a pergola.Window object implements the undo/redo feature if its hasUndo property is true (the initial value in the prototype is false), and assigns the undoManager instance property. You may want to be consistent with this.

To disable the feature temporarily you invoke the disable() method:

You can then reactivate it by invoking the register() method.

Undo is particularly needed in vector editing applications. Suppose you have defined a Line class for drawing lines; at the end of a drawing action (for example on a mouseup event) the properties of the new Line object are set in the callback function, and the line is in the DOM. At that point a memento for the action that just took place must be added to the Undo buffer by invoking the add() method. Example (contextual):

The add() method expects an array with one or more memento objects. For a single drawing action you would define only one array element; but if the action deletes a group of SVG elements (like a rubber tool, for example) you will define several array elements.

The object must define the obj reference, the undo and redo callback properties, and may define any other properties that you need to process in the callback functions. The undo and redo callbacks are invoked on keyboard events CTRL + Z or SHIFT + CTRL + Z, respectively.

The code above implies that myObject has remove() and restore() methods.

The actions to carry out in the callbacks range from the very simple to the quite complex. Rather than defining remove() and restore() methods as top level properties of an object, it can be wiser to define undo and redo object properties defining methods, for example:

In the case of a class for drawing ordinary lines, or of text input, the callback functions would be simpler. This is to stress the fact that using the undo/redo feature demands a good deal of attention in your downstream code, to ensure that your application continues to run smoothly.

KEY CLASS

The pergola.Key class does not inherit from the pergola.Class superclass.

A Key instance is designed to be assigned to a property of some object like a text box, or an input box, for keyboard input.

To activate the keyboard you instantiate the class and invoke its register() method passing an initialization object, where the handle property is required:

The instance inherits the properties specified in the initialization object. The register() method adds "keydown", "keypress" and "keyup" event listeners. It also adds "mouseup" event listener with this mousedown() handler; a click on the document stops the current typing process.

The active class property is a reference to the current typing process –the Key instance that activated keyboard input.

The release() method handles end of input following a mousedown event on the document (this can be overridden through the stop property, which we will see further down).

The release() method may also be explicitly invoked from another scope like this:

If handle has a endInput method (to process the result, like sending information to the server, modifying the document, applying a color value to a target object, etc.), it is invoked with the optional parameters passed to the release() method. In this case, the presence of parameters signals to the endInput() function that input was ended by means other than those predefined for the Key instance, letting you take appropriate actions.

The pergola.Key class defines generic class methods to handle the keyboard events: keydown(), keypress(), and keyup(). They are suitable for most situations, but for specific needs you can pass callback handlers to the register() method.

It also defines more specific class methods to handle the keypress event: keypressNum(), keypressInt(), keypressPos(), keypressPosInt(), and keypressHex(). As their names suggest, they are designed to filter the input: numbers, integers, positive numbers, positive integers, and hexadecimal, respectively.

These functions expect the handle object to have a value property and a setValue() method. If handle does not define these properties you must pass your own handlers.

Note that these functions, or your overriding functions/methods, are invoked as methods of handle, and therefore in their scope “this” is handle.

To override the default handlers you define the callback property, where you can define any or all of the custom handlers:

Note that if handle has instance or prototype methods named keydown, keypress and keyup (any, or all), you don't actually need to specify them in the callback object, as they will automatically override the default keydown/keypress/keyup functions.

The callback property can also get a function. In this case the function that you specify will be the unique handler for the three event types. This is not recommended, as it can easily generate confusion, and can lead to false assumptions in terms of browser's behavior.

Other instance properties that can be defined in the initialization object are: cursor (blinking text cursor), warn (out of range message), stop, and destroy (the string name of the property to which the Key instance was assigned).

When the stop property is statically or dynamically set to false, it will prevent some actions in the interface (acting on a component –like topbar or scrollbar for example– of the input container) from causing end of input. In practice you would never set this property to false when you invoke the register() method, and you actually don't need to set it at all; you would instead set it statically in the event handlers of those interface objects that you do not wish to interfere with the input, or set it dynamically on certain conditions. For those cases, after the interfering action has ended don't forget to set it to true (otherwise input will never end):

LIBRARIES

FILTERS library

A filter definition is a function –property of the pergola.filter object (defined in lib/filters.js). The function first creates a unique ID based on the function name and values, taking into account the overriding values, and if it doesn't already exist in the document it creates and appends the filter element to pergola.defs. Returns the URI string.

Currently there are four filter functions: blur, specular, noise, and turbulence. They can provide a very wide variety of effects by simply tweaking the attributes, as you can see in the Filters examples page.

The filter functions expect an optional object specifying filter attributes to override the default values. The property names use SVG filter vocabulary, and names containing illegal characters for JavaScript variable names are specified using the string notation, for example "flood-opacity". A numeric value can be specified as number or string. Because of the different types of fe components used to fabricate a filter, the expected attributes are not necessarily the same across the functions. In the Filters page of the API reference you can find the list of filter attributes for each of the functions.

How

You can assign the call to the filter property of an element:
filter
Fig. 5 – Filter. Rosewood texture.

or to a variable if you plan on reusing it:

PATTERNS library

A pattern definition is a function –property of the pergola.pattern object (defined in lib/patterns.js). The function first creates a unique ID based on the function name and values, taking into account the overriding values, and if it doesn't already exist in the document it creates and appends the pattern element to pergola.defs. Returns the URI string.

The pergola.pattern object is a collection of base models. To create a new pattern you can override properties and let the computer produce a wide range of variations, sometimes with truly surprising results. Most pattern definitions leverage computer logic, yielding design types that can be qualified as “modern”, while Others are more statically oriented towards human made design, like for example the two “pied_de_poule” versions, which are reflections of the traditional pattern industry, but then again, what is not logic?

The pattern functions expect an optional object specifying overriding properties. Some properties have arbitrary names, and some use SVG vocabulary (names containing illegal characters for JavaScript variable names are specified using the string notation, e.g. "stroke-width"). In the Patterns page of the API reference you can find the list of pattern attributes for each of the functions.

How

You assign the call directly to the fill and/or stroke properties of an element:
button
Fig. 6 – Pattern “hive” with transforms.
or to a variable if you plan to reuse it:

Patterns examples page.

MARKERS library

A marker definition is a function –property of the pergola.marker object (defined in lib/markers.js). Markers are not created at runtime. The function creates and appends the marker element to pergola.defs if it doesn't exist in the document. Returns the URI string.

Except for the dot marker (<circle>), for which you can specify radius, fill, and stroke, the functions don't expect parameters, and the marker cannot be modified programmatically.

How

You can assign the call to the "marker-start", "marker-mid" and "marker-end" properties. The example below (fig. 7) uses two different markers for "marker-start" and "marker-end":

marker
Fig. 7 – Markers terminalStart() and terminalEnd().
or to a variable if the marker is to be reused:

terminalStart is then "url(#terminalStart)".

Another example using "marker-start", "marker-mid" and "marker-end":

marker
Fig. 8 – Marker dot().

One more example using "marker-mid":

marker
Fig. 9 – Marker secant().

Markers examples page.

SYMBOLS library

pergola.symbols (defined in lib/symbols.js) is an object whose properties represent a collection of symbol definitions. Each property is an array with one or more object elements defining SVG primitives, or text, or image elements, in the format expected by the pergola.SVG() node builder (without the appendTo property). A symbol property can also be an object designating a category, like it's the case for pergola.symbols.arrow, defining in turn the object properties tiny, small, and large, each having a subset of symbol properties up, down, left, and right; the reference to one of those would be: pergola.symbols.arrow.small.left.

pergola.symbols is a proprietary format and is not related to the SVG <symbol> element. Example:

Note: Pergola does not create a <symbol> element in the <defs> or anywhere, and instances of it are not created by the <use> element.

You can add new symbol definitions to the pergola.symbols object in the lib/symbols.js file.

How

Use With Class Instances

A symbol instance is created by invoking the pergola.symbol() utility function, using the function's call() method. The function returns a <group> containing the symbol's elements.

In practice you don't invoke the function directly when you need to use a symbol with instances of classes that are enabled to use them. You just define the symbol property of the object. Example, a button with an arrow symbol:

symbol
Fig. 10 – symbol arrow.large.down.

The symbol object 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 (it's named “use” to avoid any equivocal relation with the <use> element).

The symbol property is an object or a string. Its value can be a reference to a symbol definition, e.g. one of the pergola.symbols definitions–or a nonreusable symbol defined inline, in the same format– or the URL of an image:

symbol
Fig. 11 – symbol referencing an image.

Note: when using images the width and height properties are required.

If x and/or y number or string are specified, these are used for the additional transformation "translate(x, y)" of the group, after any specified scale transformation. If x and y are not specified the symbol is automatically centered; this gives a satisfactory result for symbols that have the bounding box origin at 0, 0 or at least close to that, which is the case for the Pergola symbols. Note however that in some cases you may need to tweak x and y manually to get neat contours for the symbol.

Note that the fill attribute is suited for monochrome symbols.

In the attributes object property you can specify any attribute to override, or any new attribute using SVG vocabulary. These attributes are applied to the all elements, not to the group.

If the opacity attribute is specified as property of the symbol object, it is applied to the group.

Use As Standalone

Symbols can also be used as standalone objects, for example for creating a desktop icon or a logo.

Case 1, an orphan symbol:

symbol
Fig. 12 – symbol “penTool”.

Case 2, the symbol is a component of an object. Follow the same procedure as with class instances, then explicitly invoke pergola.symbol() using the function's call() method:

symbol
Fig. 13 – symbol “banana”.

Note that in this case, like for class instances, the symbol is appended to the outmost container of the object, which must be referenced by the property container. If your object does not have such a property you must specify the parent property of the symbol object, like for the orphan symbol of the previous example.

If your object has width and height properties and you don't specify x and y for the symbol object, the symbol will be centered. Note that in the example above the symbol is positioned at 40, 60 in absolute coordinates (group's translate values + symbol's x,y).

Symbols examples page.

SHAPES library

shapes (defined in lib/shapes.js) is an object whose properties represent a collection of shapes. Each object property defines the element string property –typically "path", "polyline", "polygon", but can be any primitive– and the corresponding geometrical attribute(s) ("d", "points"). A shape object does not define paint attributes.

The triangle shape definition:

The element string property represents the name of an SVG primitive.

Other properties represent the element's geometrical attributes: d for path; cx, cy, rx, ry for ellipse; points for polygon; etc. Needless to say, shapes corresponding to primitives other than path, polygon and polyline are not as useful, except perhaps for particular art compositions.

Unlike the pergola.symbols, 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 pergola.SVG(), a shape object is rendered with the specified attributes (using SVG attributes vocabulary).

How

Use as standalone

You can use a shape directly by invoking the utility function pergola.use(), which simulates the SVG <use> element. Because only the geometry is defined for shapes, you have total control over stroke width/scaling:

shape
Fig. 14 – shape “triangle”.
Use With Classes

Currently Pergola implements the use of shapes for the pergola.Button class only; its build() method applies the rendering attributes defined in the prototype and, eventually, other attributes and transformations found in the extra object.

You use a shape by defining the shape property in the initialization object. Its value is a reference to a shape object from the library:

shape
Fig. 15 – button using shape “triangle”.

Note that rotations are always applied about the shape's center and only the shape is rotated, keeping symbols, images, and text upright.

A shape object can also be defined on the fly using the same format of the library shapes:

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

Shapes examples page.

CURSORS library

pergola.cursors (defined in lib/cursors.js) is a function that defines SVG <cursor> elements.

Cursors are created and appended to the pergola.defs at runtime. The <cursor> element may be fully, partially or not at all implemented in the existing SVG implementations. In this version of Pergola, bitmap images only are used for cursors. This format has been found to have the highest level of portability at writing time.

How

Cursor are referenced by their URI string:

cursor
Fig. 16 – cursor “rotationCursor”.

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

QUICK TIPS library

pergola.quickTip (defined in lib/qtips.js) is an object defining object properties. Each object property must define the tip string or array of strings property.

Quick tips are created and appended to pergola.defs at runtime.

How

Typically used with buttons and tool buttons, quick tips are configurable:

cursor
Fig. 17 – quick tip with fixed position (no mouse tracking) and custom delay.

The required tip property is a string that can contain the escape sequence "\n" for new lines, or an array of strings.

If x and/or y number or string are specified, the mouse tracking is disabled and the quick tip shows at x,y position relative to the object's container.

If delay number of milliseconds is specified, overrides the default pop up delay (700 ms).

MESSAGES library

pergola.messages (defined in lib/messages.js) is a repository object defining string or array of strings properties. A message string (or sequence of array elements) can be passed to pergola.message() (shortcut $M) or to window.alert().

How

SKINS library

pergola.skins (defined in lib/skins.js) is an object defining skin function properties. The skin function specified in the configuration file is invoked at runtime. The function defines color scheme related system constants, the pergola.presentationAttributes object, and gradients and patterns (note that since v 2.2.00 symbols' gradients moved to the symbols.js file).

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:

The pergola.skins object currently defines two skin functions: rubber and office. A new skin would then be a new property of pergola.skins, named perhaps galaxy. The galaxy function will be structurally an exact replica of rubber or office.

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 skins. Instead, make a new skin by copying one, give it a new name and edit the new skin. Learn more in the MAKING A NEW SKIN section.

Worth of note, for those 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.color.shade() function, 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 designers/architects for getting coordinate colors, etc.

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

How Does The Skin Work?

A skin needs a theme color to work with. You set the theme color in the configuration file. You then use the pergola.color.shade() function 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 theme property is a string representing a color value specified in any legal format, including color keywords and custom keywords defined in customColorNames (located at the end of the skins.js file).

HOW TOs

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 aren't used yet due to the notorious rendering speed limitations. They may be progressively added as the browser's and hardware performance increases. The current skins effects are largely based on gradients.

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 you need neutrals (black, white, grays), which are known to work with most base colors. These shades are applied to fills, strokes and gradients and are computed by the skin engine.

You set the skin and theme of a project in its config file:

See 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 you have perhaps just made and that you named galaxy, you assign the value "galaxy" to the skin property:

That's all. Your 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.

I'm All Set, I Want To Make A New Skin.

OK, you want to make a new skin named galaxy. The best way to proceed is this:

  1. Copy the rubber skin function, complete with its gradients definitions.
  2. Paste below as a new property of pergola.skins and name it galaxy.
  3. Set the theme color to the base color you have in mind for galaxy.
  4. In the 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.color.shade()?

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 bluish 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.

Why The Call To pergola.color.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 shade of the color and a negative value, a darker shade. 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.

LOADING SVG DOCUMENTS

The pergola.loadSVG() function loads a remote SVG document or fragment and appends it to the specified container. Browsers that implement XMLHttpRequest 2 also load local documents, otherwise the browsers' security restrictions apply.

pergola.loadSVG(object)

object defines these properties:

The function issues a request using the GET method for the specified url and appends the document to the specified target if target is a container element. If target is a pergola.Window object the document will be appended to the specified container (optional), or to the default container (window.doc.transformable).

If progress is specified the referenced progress bar will be activated. If merge is specified and true, the new document will be merged with existing content, otherwise it will replace the content of the destination container (whether a layer or window.doc.transformable). If resetScale is specified the window's zoom value will be reset to 1:1. Other than for a window context, container, merge, and resetScale do not apply.

Example from a window instantiation context:

ADDING WINDOW TOOLS

The addTools() prototype method of the pergola.Window class allows to add tools (pergola.ToolButton objects with their callback functions) at instantiation time or at any stage after construction of the window.

Tools are added as groups of one or more tools. You cannot add a single tool not belonging to any group (this restriction may be removed in a future release).

In the example below a subset of vector editing tools are added by invoking the addTools() method. The tool definitions are properties of the object parameter passed to the function. In this case the call is executed within the context of the window instance (in the body of the function assigned to the window's contains instance property), and the tools are added during the window's construction process.

tools
Fig. 18 – tool buttons with symbols.

You can defer the addition of tools by invoking the addTools() method at any stage:
myWindow.addTools().

If the separator property is specified and true, a pergola.Separator object is added between the current group and the previous one.

For each property name in drawGroup (excluding separator) a pergola.ToolButton instance is assigned to a property (with the same name) of this pergola.Window object and appended to the window's toolBar. A reference to it is added to the window's tools array, and to the exclusiveTools array if the exclusive property is set for it.

CREATING A SYSTEM MENU

If you create a system menu, which is independent from windows' menus that you eventually create, it must be assigned to the pergola.menu property, and you must assign pergola to the owner property, and pergola.menubar.container to the parent property. Example of system menu "File" with new, open and save items:

menu
Fig. 19 – system menu.

The dialog panels referenced in the code above do not necessarily need to be properties of some object, they can be top level orphan panels, though it's wiser to keep them in a namespace.

The same goes for the referenced methods, they could be top level functions or inline function literals. In this example the getFiles() method at line 28 is a method of the myApp object, while the saveDocument() method at line 35 is specified as string, implying that it's either an instance method of the save Pergola.MenuItem object or a method of its prototype, or a prototype method inherited by the Pergola.Class superclass. Learn about this mechanism in the User Events And Functions section.

CUSTOM CONTEXT MENU

Pergola implements custom context menus to override the browser's default context menu. You can define a context menu for random elements and for objects. Right mouse button events disable the browser's context menu only for the element or object's component to which the custom context menu is attached, not for the whole document, meaning that a mouseup with right button on the element displays the custom context menu, while anywhere else on the document it displays the browser's context menu.

How

Context menu for an element

To cater for this Pergola provides the pergola.contextmenuManager object defining the methods browser(), custom(), and activate(). You normally only need to invoke activate(), which lets you set a custom context menu for any element, acting on the event target.

When defining a context menu for a random element you must provide event handling for the right mouse button for that element. Note that you must not create an instance of the pergola.ContextMenu class; you only need to define the contextMenuItems object property of the element. The pergola.contextmenuManager.activate() method creates the pergola.ContextMenu instance, if it doesn't exist (lazy loading).

The example below (in folder Examples/Contextmenu/contextmenu.svg) is rather crude and perhaps it's not the best idea to imitate, but it simply and clearly shows how to define and activate a context menu. The handler tests the right mouse button and invokes the pergola.contextmenuManager.activate() function passing three parameters: the event, the handler (simply a (self) reference to the current handler), and an object defining the context menu items:

contextmenu
Fig. 20 – custom context menu.

Note: in the event handler the test for the right mouse button must return, and must be carried out before other processing, if any.

Context menu for an object

Currently you can attach a custom context menu to instances of these classes:

You can also attach a custom context menu to these components of Window instances:

Context menu for Panel components is currently not implemented.

When defining a context menu for an object you must not create an instance of the pergola.ContextMenu class; you only need to define the contextMenuItems object property for that object. The setContextmenu() method creates the pergola.ContextMenu instance, if it doesn't exist (lazy loading).

Attaching a custom context menu to Button, Taskbar, ToolBar instances at instantiation:

or deferred:

Attaching a custom context menu to components of Window instances at instantiation:

or deferred:

Note that if the system already defines context menus for a window component, your context menus will be added after those (a separator is automatically added).

Note: the callback functions of the context menu items are executed in the context of the object that defines them, not that of the ContextMenuItem instance; in the code right above for example, this, in the fn callbacks, is the myWindow.status object.

THE FIND DIALOG

Pergola creates the pergola.findDialog, an instance of the pergola.Panel class, with lazy loading.

The best practice for initializing and opening the Find dialog is to invoke the find() prototype method, if you are working with instances of classes inheriting from Class, or the pergola.find() utility function (as a method of the object requiring the service by using the function's call() method) in any other context.

Find dialog
Fig. 21 – the Find dialog.

The find() function expects a callback parameter that you can specify as a string (method name) or function (see the User Events And Functions section). The find dialog is created if it doesn't exist, and initialized with this as user and callback as callback. The latter is invoked on clicking the panel's OK button. The initialization looks like this:

  object.find(function);
or
  pergola.find.call(object, function);

To retrieve the dialog's information the panel defines the getData() instance method. The call to this method would tipically be the first statement in the callback function:

  var result = pergola.findDialog.getData();

The function returns an object with these properties:

You can then process the data. Here's an example of how this is done by the search() prototype method (the callback) of the pergola.DataGrid class:

and the associated methods:

THE FILE DIALOG

Pergola creates the pergola.fileDialog, an instance of the pergola.Panel class, with lazy loading.

The best practice for initializing and opening the File dialog is to invoke the getFiles() prototype method, if you are working with instances of classes inheriting from Class, or the pergola.getFiles() utility function (as a method of the object requiring the service by using the function's call() method) from any other context.

File dialog
Fig. 22 – the File dialog.

The getFiles() function expects an object with these required properties:

and the optional property:

The file dialog is created if it doesn't exist, and initialized with this as user and callback as callback. The latter is invoked on clicking the panel's OK button.

The title and ok properties are required and you get an alert if they're missing. It's very easy to miss a title bar that says "Open" while the OK button says "Save".

Note: after the request the files array property contains the updated list of files in the specified path folder. The initial value of this files would typically be the empty array.

Note: a copy of the script designated by the getDir property must exist in the root forlder of your application, whether your own script, or the default pergola.getDir script (getDir.php, provided in the lib/PHP folder).

The initialization can look like this:

In the callback function that you specify you can query the result (the file name) like this:

and then do something with it.

ADDING A LOGO ICON

To place your logo or the application's logo as icon on the pergola.taskbar you can create a pergola.Button specifying its symbol property object, or invoke directly the pergola.symbol() utility function passing a symbol object. In both cases the symbol's artwork can be SVG artwork or an image. The symbol's artwork can be defined in the Symbols library or defined inline (using the library's format).

Using a button:

logo icon
Fig. 23 – logo icon on taskbar.

Note: the opacity : 0 at line 11 prevents the button's highlight on hovering. The button solution allows using interesting effects like those of the monaLisa in the buttons examples. A future version will allow adding filters to the button's mask and/or button elements.

Using the pergola.symbol() function, as shown in the Symbols section. Example with an image as symbol:

MAPS

Pergola displays maps within windows. For this, it uses a custom version of the Polymaps 2.4.0 library. The modifications were necessary to make the Polymaps library function in a standalone SVG context. At this stage true multiple instances (different maps in different windows) are not yet possible, in that a new instance in a new window will end up being an exact replica of the other. Currently only Bing maps are supported.

Case study in “Building Web Applications with SVG” (Microsoft Press).

The Window prototype is extended with specific mapping utilities, and the behavior of the transformation tools, including scrollbars, overrides the regular behavior by sending tile requests rather than acting on the contained document's viewport. You define a mapping window through its type property: "map".

A map is instantiated as a property of a newly created window. The window can be populated with a map either manually, as per the Polymaps documentation, or by invoking the prototype method mapMaker through the contains property.

With the first technique you have full control over the Polymaps features:

Using the contains property is easier, but the implemented Polymaps features are preset:

However, other Polymaps features can be added like this:

bingWin.map.add(polymaps.feature());

Note that Polymaps navigation buttons are disabled and replaced by Pergola tool buttons. Keyboard navigation is always available.

See the BingWindow.svg example in folder Examples/BingMaps/ with map layers and features in the window's menu.

DATAGRID

A pergola.DataGrid object has these features:

A datagrid is wrapped in a window, therefore you must first create a pergola.Window object in the usual manner, specifying its "datagrid" type:

And then invoke its datagridMaker() method:

See the complete list of pergola.DataGrid prototype properties in the API reference.

The required binding property gets the URL string of the JSON file to load, or a JavaScript object. The latter lets you work in local mode (you would typically assign the object to a variable/property in a separate data file, like in the example). The interest of local mode is when you need to make a file by hand, for whatever reason; it lets you check its validity as you edit. Note that the JavaScript object doesn't have to be a JSON object, i.e. you can use regular JavaScript property naming conventions, not just the string notation; then, when loading the file in remote mode and saving, it will produce the name.json file containing the JSON object.

All other pergola.Datagrid prototype properties can be overridden here, or let the datagrid use their default values. Worth of note is the rowHeight property: with its initial value "auto" the engine computes the maximum cell height and that value is applied to all cells of a same row; or you can specify a number (user space units) representing a fixed row height.

Note: the current document can be overwritten only if the permissions property is set to "administrator"; the default value "user" only allows to Save As.

Data Format

The native format for the datagrid is JSON. The JSON object must define the "columns" and "rows" array properties. The example below shows a table with 7 columns and 4 rows:

Producing a JSON file

  1. Start by editing a regular JavaScript object literal by hand in local mode.
  2. Assign your JavaScript object to the binding property.
  3. Run the datagrid in remote mode, and Save As file name.
The saved file contains the JSON object.

Columns

Each element of the "columns" array is an object.

The value of the required "label" property shows in the column's header.

The required "type" property specifies the type of data in the cells; it can be one of: "string" "date" "number" "boolean".

Other optional properties are specific to types and are reviewed in the Data Types section further down.

Rows

Each element of the "rows" array is an object. Its property names matching the values of the columns' "label" properties are considered; any others are ignored and don't produce errors. Therefore the number of fields in the rows doesn't have to match exactly the number of columns. This eventually allows you to build partial datagrids from a unique dataset. For instance, say that you have a dataset where each row contains 20 fields and that you need to build a datagrid showing 7 fields: you then simply specify in the Columns which fields to use (the "label" property).

Note that pergola.DataGrid works internally mostly with objects and therefore the order of the properties in "rows" is not important, though it's certainly neater, visually, to have them in the same order.

Data Types

string

Values of type "string" are not processed.

number

For columns of type "number" the datagrid uses the Intl.NumberFormat where available (all major implementations except Safari, at writing time), or the toLocaleString() method.

The optional "options" property is an object as specified by Intl.NumberFormat options (see Mozilla's Intl.NumberFormat for a more friendly language). In the Salary column of the JSON object above, for example, the currency is specified using a ISO 4217 currency code ("USD, "EUR", "JPY"...); the currency symbol is included in the locale string of the input box (or text box in readOnly mode).

When editing cells of this type the field is set to its numeric value (stripped of any locale formatting options) and expects a JavaScript number as input–the keypress callback for this type is set to the keypressNum() class method of the pergola.Key class. On input validation the value of the cell (in Rows) is converted live back to (and saved as) type number; the value of the input box is formatted with the locale options.

Note: when processing the Intl.NumberFormat "currency" style option, only IE displays trailing decimal zeros, ex: $ 1500.00.

Note: at writing time Safari ignores formatting options.

date

For columns of type "date" the datagrid uses the Intl.DateTimeFormat where available (all major implementations except Safari, at writing time), or the toDateString() method. When editing cells of this type the field is reset and expects a valid date parameter for the Date constructor. The accepted formats are ISO and RFC.

For columns of type "date" you can specify the optional "options" property, an object as specified by Intl.DateTimeFormat options (see Mozilla's Intl.DateTimeFormat for a more friendly language).

Note: at writing time Safari only accepts RFC dates, and ignores formatting options.

boolean

The value of "boolean" cells is converted to a string and back to Boolean on file saving. Columns of this type too can be sorted. For a column of this type you can use The optional "options" array property, containing exactly 2 string elements specifying replacements for true and false, e.g. ["Yes", "No"].

The null value

Due to the controversial (and much controverted) nature of null in JavaScript, this value is always converted to string and converted back to null on file saving. As a general rule it would seem more appropriate to use the empty string instead of null when the value of a field is unknown. A thing that may be seen as an advantage in using the empty string is that, whether sorting ascending or descending, empty fields always show at the bottom.

USING THE DEBUGGER

Pergola implements a debugger that you can use instead of the system alert() for checking variable/property state.

To activate the debugger follow the instructions in the Configuration chapter.

How

$D() is a shortcut for pergola.debugger.vars().

KNOWN ISSUES

1. SMIL

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

Workaround
No known workaround.

2. External Reference In Loaded 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.

Workaround
Set the URL relative to the SVG file that loads the map, or use absolute URL.

3. Loading Local Documents

Loading local documents is only allowed in browsers implementing XMLHttpRequest 2.

4. Markers

IE 9+: glitches with markers manipulation in live drawing.

Workaround
No known workaround.