Quantcast
Channel: XPages – Xcellerant
Viewing all 216 articles
Browse latest View live

Dojo in XPages 12 – Animating DOM Elements with Simple Actions

$
0
0

One of the great features of Dojo is that it provides the ability to animate DOM elements. The easiest way to make use of that functionality in an XPages application is the built-in simple actions. In this post, we’ll look at the actions that are available and how to use them.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Animation Simple Actions

The easiest way to use common Dojo animations is to use the simple actions that are part of the Extension Library (and included with Notes 8.5.3 Upgrade Pack 1 and built into Notes 9).

To find them, select a component to which you can add an event handler. On the Events view, click the Client tab, ensure that Simple Actions is selected and click Add Action...

Change the Category drop-down to Dojo Effects and you’ll see a list of options.

Dojo_12a

  • Dojo Animation – Animates one or more attributes
  • Dojo Fade In Effect – Fades in a hidden element
  • Dojo Fade Out Effect – Fades out an element
  • Dojo Slide To Effect – Slides an element to a specific position
  • Dojo Wipe In Effect – Fades in a hidden element with a wipe in effect
  • Dojo Wipe Out Effect – Fades out an element with a wipe effect

Fading

All you have to do to use the fade and wipe actions is define the target node. You can choose an XPages component or define a hard-coded or computed client-side element ID.

For a page with a div that has an id of “mydiv”, these simple actions will fade the div out and back in:

Dojo_12b_FadeOut

Dojo_12c_FadeIn

The fade effects steadily change the opacity of the element from 1 to 0 and vice versa.

The duration specifies how long the animation will take to complete (in milliseconds). The default is 350 milliseconds. The easing property can take a javascript function that defines the rate of the animation. You can cause it to speed up or slow down or modulate.

Wiping

Similarly to the Fade animations, you can define the target node and the Wipe actions will work.

However, because this animates the height, be careful of using this with div tags. Once you wipe a div out, its height attribute is changed to auto, rather than its originally-defined. When you try to execute the Wipe In effect, there’s no height defined for the div, so it doesn’t display anything (aside from a small line during the animation, which goes away)! This appears to be the case both when the div is styled inline or with a class, using CSS. (The same issue does not seem to apply for other elements.)

SlideTo

The SlideTo animation moves an element on the page to a specific position, as defined by the top and left properties.

Dojo_12d_SlideTo

You can change the duration to speed it up or slow it down. You can also define an easing function to modify whether it speeds up or slows down.

Custom Animation

Aside from the built-in effects mentioned above, you can use the Dojo Animation action to animate other properties.

In addition to specifying the target element, you also define one or more properties to animate and the end state of each property. (It will start with the element’s current attributes if you don’t define start parameters.)

Here’s an example that will change the div color from red to blue and change its height from 100px to 200px:

Dojo_12e_Custom

NOTE: As mentioned in other recent posts, Dojo attribute names are very similar to CSS attribute names, but they do not allow hyphens. The background-color css attribute is defined as backgroundColor for this animation.

You can set the repeat property to perform the animation mutiple times, although it appears that you have to define the starting values if you do that. And the element ends up in its original state, rather than in the defined end state.

You can also set the frame rate and animation duration. You can also set a delay before the animation begins.

Up Next

In the next post, we’ll look at the dojo.fx module and see how to generate these animations with client-side code. This will allow you to further customize the animations and also to attach them to non-XPages elements.



Dojo in XPages 13 – DOM Element Animation Functions

$
0
0

In the last post, we saw how to animate DOM elements with simple actions built into XPages. In this post, we’ll look at the underlying Dojo code they generate and see how to create them without the simple actions, which allows you to add animations to be triggered by non-XPages components and lays the foundation for creating fully-customized animations.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Using Built-In Animation Methods

Dojo has a some common methods built in for easy use.

The general concept is that you call an animation method and define its parameters, then call the play() method to run it.

Fade Out

This is the code that is generated by the simple action:

function view__id1__id4_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fadeOut({"node":_id});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fadeOut({'node':'myDiv'}).play();

This is not the recommended syntax, but it seems to work as well:

dojo.fadeOut('myDiv').play();

The initial syntax is required when you want to add more attributes to the animation. To change the duration of the animation, specify the value in milliseconds. Here are two ways you can write this:

fadeParameters = {'node':'myDiv', 'duration':1000};
dojo.fadeOut(fadeParameters).play();
dojo.fadeOut({'node':'myDiv', 'duration':1000}).play();

Fade In

Here’s the code generated by the simple action:

function view__id1__id5_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fadeIn({"node":_id});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fadeIn({'node':'myDiv'}).play();

Interestingly, this does not seem to work, even though it works for fadeOut()

dojo.fadeIn('myDiv').play();

To change the duration of the animation, add the duration attribute.

dojo.fadeIn({'node':'myDiv', 'duration':1000}).play();

Transitions in dojo.fx

The wipe transitions do not have corresponding methods in core dojo, but they do exist in the dojo.fx module.

When you use any of the simple actions, it automatically includes the module for you. It adds this script tag in the page header:

<script type="text/javascript">dojo.require('dojo.fx')</script>

If you’re not using a simple action transition on the page, then you’ll need to include the module yoursef. If you don’t, you’ll see a ‘dojo.fx is undefined’ error in the developer tools console:

Dojo13_a_fxError

You can include the module on your page or at the application level via the theme. At the page level, go to Resources > Add > Dojo module... and enter the module name.

Dojo13_b_Include_fx

This puts the same script tag shown above into the page header.

Wipe Out

Here’s the code that’s generated for the Wipe Out simple action:

function view__id1__id6_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fx.wipeOut({"node":_id});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fx.wipeOut({'node':'myDiv'}).play();

Wipe In

The Wipe In method is very similar, but it also needs a parameter for the final height of the object.

Here’s the code generated by the simple action:

function view__id1__id7_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fx.wipeIn({"node":_id,"height":100});
  _a1.play();
}

Here’s how you can write it yourself:

dojo.fx.wipeIn({'node':'myDiv','height':100}).play()

Slide To

The Slide To function moves an element by animating the top and left position to the desired place on the page.

Here’s the code generated by the simple action:

function view__id1__id10_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.fx.slideTo({"node":_id,"top":400,"left":400});
  _a1.play();
} 

Here’s how you can write it yourself:

dojo.fx.slideTo({'node':'myDiv','top':400,'left':400}).play();

Up Next

These built-in methods make it easy to animate some elements on the page but don’t provide much opportunity for customization. In the next post, we’ll take a look at the method you can use to define custom animations on your page.


Dojo in XPages – 14: Custom Animations

$
0
0

In the last post, we looked at the dojo code to perform pre-defined animations. In reality, those are wrappers for the actual animation function. In this post, I’ll show how to animate any DOM element property so you can create your own custom animations.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

dojo.animateProperty()

The Fade In/Out methods animate opacity attribute, the Wipe In/Out methods animate the height attribute, and the Slide To method animates the top and left attributes of an element.

The built-in methods are handy, bu you can write these same animations yourself pretty simply as well. This is good news because you can also easily modify the behavior as needed.

The animateProperty() method allows you to do this. It is part of core dojo — you don’t need to load the dojo.fx module in order to use it.

Custom Animation Simple Action Source

In a previous post I showed the simple actions that are available to create Dojo animations in XPages.

The custom animation shown in that post changed the background color (from red to blue) and the height (from 100px to 200px) of a simple div on the page.

Here’s the code that the simple action generated:

function view__id1__id9_clientSide_onclick(thisEvent) {
  var _id=dojo.byId("myDiv");
  var _a1 = dojo.animateProperty({"node":_id,"properties": {"backgroundColor": {"start":"#FF0000","end":"#0000FF"},"height": {"start":"100","unit":"px","end":"200"}}});
  _a1.play();
}

Here’s how you could write that yourself to perform that same animation

dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "backgroundColor": {"start":"#FF0000","end":"#0000FF"},
    "height": {"start":"100","unit":"px","end":"200"}
  }
}).play();

Another Example — Changing the Wipe Direction

The built in methods (in dojo.fx) to wipeIn() and wipeOut() an element work by modifying the height of the element. If you’d like to create a horizontal wipe effect, you can animate the width property instead.

This will wipe it out horizontally:

dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "width":{"end":"0"}
  }
}).play();

This will wipe it back in horizontally:

dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "width":{"end":"200"}
  }
}).play();

Additional Animation Properties

In addition to the required animation attributes, you can also define a delay before starting the animation, the duration of the animation (default is 350 milliseconds) and an easing function to modify the effect. (We’ll come back to that in the next post.) You can also change the frame rate and a repeat property to play the animation more than once.

Animation Events

I’m not going to dig into it here, but there are 5 animation events to which you can attach handlers if you need to trigger another action:

  • beforeBegin
  • onBegin
  • onEnd
  • onPlay
  • onAnimate

Combining Animation of Mulitple Elements

If you want to animate multiple elements concurrently, you can define the animations as objects and use the combine() method to run them together. Since this method is part of the dojo.fx module, you’ll need to include that module on the page.

For example, if I have two divs on my page (myDiv and myOtherDiv) and I want to make the first one smaller and the second one larger, I can define both of those animations and chain them together like this:

var div1Animation = dojo.animateProperty({
  "node":"myDiv",
  "properties": {
    "width":{"end":"50"}
  }
});

var div2Animation = dojo.animateProperty({
  "node":"myOtherDiv",
  "properties": {
    "width":{"end":"300"}
  }
});

dojo.fx.combine([div1Animation, div2Animation]).play();

The combine() method takes an array of animation objects.

Up Next

At this point, you’re armed to create your own custom animations! In the next post we’ll look at the built-in easing methods available to modify the timing of how the animations play out.


Dojo in XPages – 15: Modifying the Rate of Animation with Easing Functions

$
0
0

In the last few posts, we’ve looked at dojo code to perform pre-defined animations and custom animations. By default, the animations progress in a linear fashion, but the rate of animation can be changed. In this post, we’ll take a look at the easing functions that are available to modify animations.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Built-In Easing Functions

The easing property of an animation defines the rate of the animation.

You can write your own function to use, but there are 31 built-in functions already available to save you some time.

The default is linear – it performs the transition evenly throughout. The other methods are mathematical calculations that change the rate of the transition at the beginning or end (or both).

Easing Function Effect on Transition
linear No effect — even throughout
quadIn, quadOut, quadInOut rate by power of 2
cubicIn, cubicOut, cubicInOut rate by power of 3
quartIn, quartOut, quartInOut rate by power of 4
quintIn, quintOut, quintInOut rate by power of 5
sineIn, sineOut, sineInOut rate by sine function over time
expoIn, expoOut, expoInOut exponential effect
circIn, circOut, circInOut circular effect
backIn, backOut, backInOut start away from target, then move toward it
elasticIn, elasticOut, elasticInOut back and forth, then toward target
bounceIn, bounceOut, bounceInOut bounce near the beginning or end

Naming Convention

The functions are all named consistently. They specify the affect and at which end(s) the effect will be applied.

  • The *In functions start slowly and then speed up toward the end
  • The *Out functions start faster and end slowly (the opposite of the *In functions)
  • The *InOut functions start slowly, speed up, then end slowly

Dojo Modules to Include

The easing functions are part of the dojo.fx.easing module, so you’ll need to include it on your page in order to use them. (This is in addition to including dojo.fx if you’re using a function in that module.)

Example

Here’s an example that will take a div called myDiv on the page and move it, using the bounceIn easing function:

dojo.fx.slideTo({
  node:"myDiv",
  top: 100,
  left: 400,
  easing: dojo.fx.easing.bounceIn,
  duration: 3000
}).play();

More Examples

There’s a good demo already set up in the Dojo documentation that allows you to test each one of these easing functions.

https://dojotoolkit.org/reference-guide/1.8/dojo/fx/easing.html#examples

Just click the Play button and you can choose a function to try. Test them out and use whatever suits your needs best


Dojo in XPages – 16: Attaching Event Handlers with dojo.on()

$
0
0

In this post, I’ll show how to dynamically add event handlers to one or more events on one or more DOM elements with dojo.on().

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Including the dojo.on module

The dojo.on module is not automatically available, so you need to add it either to the page/custom control as a resource or to the entire application via the theme. (See this post for more information).

This allows you to dynamically add event handlers as needed.

Using dojo.on()

To attach an event handler, you use the method in this form: dojo.on(DOM element, event, function)

For example, to add an event handler when the user clicks a button with the id of myButton, you could use this code:

dojo.on(dojo.byId('myButton'), 'click', function() {
  console.log('clicked button');
});

You could also define the function by name rather than inline.

function logClick() {
  console.log('clicked button');
}

dojo.on(dojo.byId('myButton'), 'click', logClick);

This allows you to refer to more complex functions or functions that exist in libraries or elsewhere on the page.

Adding Event Handlers to Multiple Elements

You can also add event handlers to multiple elements at the same time by chaining the on() event to a dojo.query().

This code will add a click event handler to all table rows on the page:

dojo.query('tr').on('click', function() {
  console.log('clicked table row');
});

Since you’re chaining this to a query that’s selecting the elements, you only have to specify the event and the function when calling the on() method.

Event Handler Timing

The timing of connecting events is very important. If you run this code immediately as the page loads, the DOM elements may not be available for attaching event handlers.

If you need the code to run right away, put it in the onClientLoad event of the page or custom control. This is the dojo event that fires once the page is fully loaded.

Otherwise, you can run it dynamically when triggered by some other page event.

Adding Handlers to Multiple Events

You can attach an event handler to multiple event types with a single call by comma-delimiting the event names.

This code will attach an event handler to the click, double-click, and mouseover events and also demonstrate how to determine the type of event that was triggered:

dojo.on(dojo.byId('myButton'), 'click, dblclick, mouseover', function() {
  console.log('triggered button event');
});

We’ll look more at dealing with event info in the next post.

Disconnecting an Event Handler

If you want to disconnect an event handler, you can keep a handle to it at the time you attach it and then use remove() method to drop it.

This code will set up an event handler and store it in a variable.

var handle = dojo.on(dojo.byId('myButton'), 'click', function() {
  console.log('clicked button');
});

This code will remove the event handler:

handle.remove();

Firing an Event Handler Once, then Automatically Disconnecting

If you want to attach an event handler that will only fire one time and then remove itself, you can use the dojo.on.once() method.

This code will add an event handler to write a message to the console on the first click and then stop listening for the event.

dojo.on.once(dojo.byId('myButton'), 'click', function() {
  console.log('clicked button');
});

Backwards Compatibility Note

The dojo.on() method is new as of Dojo 1.7, so it’ll work in Notes 9.

The previous method is dojo.connect(), which still works for now, but is deprecated and may be removed when dojo 2 is released. (Although this is not really a concern for an XPages application until Notes starts including Dojo 2.)


Dojo in XPages – 17: Getting Event Information from Event Handlers

$
0
0

In the last post, we looked at how to dynamically attach event handlers to DOM elements with Dojo. In this post, we’ll take a look at the information available to the event object that is available to the event handler.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Getting The Event Object

If you add a parameter to the event handler that you attach to any DOM element via Dojo, you will have a handle to an Event object containing informomation about the event.

Here’s an example:

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  // do something
});

Event Coordinates

The layerX and layerY properties provide the coordinates of the event (such as a click) relative to the event target and the pageX and pageY properties provide the coordinates of the event relative to the page.

If the element flows inline on the page, then the layer and page coordinates generally seem to be the same. If the element is absolutely positioned on the page, then the layer properties are relative to the “layer” that is absolutely positioned.

You can test these properties with code like this:

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  console.log('clicked div');
  console.log('layerX: ' + e.layerX);
  console.log('layerY: ' + e.layerY);
  console.log('pageX: ' + e.pageX);
  console.log('pageY: ' + e.pageY);
});

To see the difference in the layer properties, change the div from something like this:

<div id="myDiv"
  style="height:100px; width:100px; background-color:red;">
</div>

…to this (specifying the positioning):

<div id="myDiv"
  style="height:100px; width:100px; background-color:red; position:absolute; top:50px; left:50px;">
</div>

target vs currentTarget

The target property is the DOM element that originally received the event. The currentTarget property is the DOM element whose event handlers are currently being processed. They will be the same if you click on one element and the event does not trigger any other handlers.

Here’s an example of how they can be different. If you have a div on the page with the id of myDiv and you run the code below to attach a click event handler to both that div and the page body, you can click on the div and it will fire the click event on both the div and the page body (since they’re both listening for the click event).

In this case the event handler for the div will fire first and will show both the target and currentTarget to be myDiv. Then the body‘s event handler will run and it will show the target to be myDiv, because that was the element that first received the event.

dojo.query('body').on('click', function(e) {
  console.log('clicked page body');
  console.log('target: ' + e.target.id);
  console.log('currentTarget: ' + e.currentTarget.id);
});

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  console.log('clicked div');
  console.log('target: ' + e.target.id);
  console.log('currentTarget: ' + e.currentTarget.id);
});

Event Bubbling

As we saw in the previous example, the click event will be fired for both the div and the body that have handlers.

If desired, you can stop this bubbling behavior with the stopPropagation() method of the event.

dojo.on(dojo.byId('myDiv'), 'click', function(e) {
  console.log('clicked div');
  console.log('target: ' + e.target.id);
  console.log('currentTarget: ' + e.currentTarget.id);
  e.stopPropagation();
});

This prevents the event from being passed on to the body‘s event handler.

The preventDefault() method of the event object will prevent default behavior from firing after the event handler. For example, if you have a link, but you attach a handler to the click event and then call e.preventDefault(), the link will not be opened.

This code will log a console message and disable any link on the page:

dojo.query('a').on('click', function(e) {
  console.log('clicked link');
  e.preventDefault();
});

Reading Key Presses

Another useful piece of information to trap with event handlers is a keystroke. The charCode() method will return a character code for a printable character and the keyCode() method will return a character code for a non-printable character, which is very handy to trap special keys.

This code will display the charCode or keyCode for anything typed into an edit box named inputText1. It will also trap the Enter key and prevent it from automatically submitting the page.

dojo.query('[id$=inputText1]').on('keypress', function(e) {
  console.log('keypress');
  console.log('charCode: ' + e.charCode);
  console.log('keyCode: ' + e.keyCode);
  
  // Stop [Enter] from submitting form
  if (e.keyCode == 13) {
    console.log('trapping [Enter] key');
   e.preventDefault();
  }
});

If you want your keyCode-checking code to be a little more readable, you can include the dojo.keys module and compare the keyCode to a list of constants. (See this page for more information.)


Dojo in XPages – 18: Event Delegation

$
0
0

We recently saw how to dynamically attach event handlers to DOM elements with dojo.on(). In this post, we’ll see how to use event delegation to set up event handlers that will work on DOM elements that don’t even exist on the page yet. This is powerful logic that is well worth learning.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Event Binding vs Event Delegation

When you use the examples in the previous post to attach event handlers to DOM elements with dojo.on(), you’re binding that event handling function to the selected element(s) currently on the page. However, if the elements on the page change and new elements are later created, they will not have the event handler bound to them.

Event delegation solves this problem by looking for the desired elements from a higher level (for example, the page body or the window object). This way, if elements are added later that meet the selection criteria, they will still handle the event as desired.

Check out this post by Marky Roden for further explanation.

A Use Case for Event Delegation

A common XPages use case will illustrate the point. Take, for example, a repeat control (that creates a table row for each instance) or view panel that uses a pager.

If you run this code on the page, you will get an event handler that writes a message to the browser console each time a table row is clicked.

dojo.query('tr').on('click', function() {
  console.log ('Clicked Row');
});

However, if you use the pager to load the next set of rows, then nothing will happen when you click on a row. This is because the event handler was bound to all table rows that existed when the code was run. Once the pager is clicked, those rows were removed and a new set was created on the page.

If you want your function to run any time any row in the view or repeat is clicked, even after paging, you can use event delegation.

The code is very similar, but instead of just specifying an event to handle, you specify also specify a selector to choose the elements as well as the event to handle, in this form selector:event. The other difference is that the first parameter will not be the elements to select, but rather a higher-level element that will contain the elements that you want to handle.

It can take one of these forms:

dojo.on(window.document, [selector[:[event], function() {} );
dojo.query('body').on([selector]:[event], function() {} );

(You can also substitute the name of an existing function in place of the anonymous inline function declaration.)

For example, this will delegate the event by putting the listener on the page body. It will listen for a click event on all tr tags within the page body, regardless of whether they exist now or are created at a later time via client-side code or a partial refresh. (If the entire page is refreshed, then the handler would go away.)

dojo.query('body').on('tr:click', function() {
  console.log('Clicked Row')
});

The selector uses dojo.query(), so the selector will follow the same rules used for selecting elements with dojo.query().

Here’s another example that will do the same thing, but will save the first call to dojo.query(), so it would seem like it should be more efficient. This uses the dojo.on() method directly (rather than chained to a dojo.query()). Rather than querying an element, you can just set it to the top-level window.document, so it will listen for events on the selected elements anywhere on the page.

dojo.on(window.document, 'tr:click', function() {
  console.log('Clicked Row');
});

However, if you want to target the listening to a smaller area of the page, you may want to instead use dojo.query() to narrow the scope of listening for the events and elements.

Note that you need to include the dojo.on module on the page if you want to use the dojo.on() method in XPages, but you don’t need to include any modules if you use dojo.query().on().


Fixing an “Object has been removed or recycled” error when searching a view data source

$
0
0

I troubleshot a maddening issue in an XPages application recently, so I’m documenting it in case anyone else comes across it (or has a better solution).

The Problem

In a pretty straightforward application, I have repeat controls with view data sources. The search property of each data source is set to read from a scope variable. (When the user executes a search, the button will build a search string and store it in the scope variable.)

I received numerous reports of an “Object has been removed or recycled” error when executing searches. Sometimes, users would report null pointer errors.

There was no pattern — the same search that triggered the error would work immediately after the error was thrown when the page was refreshed. The same search would work fine the next time.

I could never reproduce the error, but as soon as several testers hit the application, they would see it fairly frequently.

Troubleshooting Steps

I’m well aware of the conflicts that can happen (even across scripts) by recycling Notes/Domino objects, but there’s not a whole lot of code and virtually none of it has to recycle anything! But I took all recycle statements out anyway and the problem did not go away.

I also made sure that every piece of code (even on every computed field just reading from a view column) was wrapped in a try – catch block to see if there’s a trappable error, but no script caught anything.

The Solution

So, I searched for similar reports of the error and found an unrelated issue (related to the pager save state control in a data view), but the solution mentioned in the comments sounded like something worth trying.

Changing the application-level Server page persistence property (in XSP Properties) from “Keep pages in memory” to “Keep pages on disk” solved the problem immediately.

Page Persistence

Is there a better solution? It seems like a pretty significant change to have to make when that may not be the best option for the application overall.



Implementing Font Awesome 4.0.3 in XPages

$
0
0

Font Awesome is a great library of icons (implemented as a font) that you can use in your applications. Russ Maher wrote a great post and provided a sample application to make it very easy to get up and running with Font Awesome quickly. If you want to use version 3.2.1, stop reading this post and follow Russ’ instructions, because that’s the fastest way to go. However, Font Awesome is now up to version 4.0.3 and if you want to use that (or any future version), you can follow the process outlined here — including an XPages-specific gotcha.

Adding Font Awesome to Your Application

1) Download Font Awesome

http://fortawesome.github.io/Font-Awesome/

2) Extract the files somewhere on your hard drive

3) Add the files to your application

Open in Package Explorer or Navigator in DDE. Drag the font awesome folder from the file system into the WebContent folder in your application.

FontAwesome_WebContent

4) Include Font Awesome CSS

You need one of two stylesheets: font-awesome.css or font-awesome.min.css.

You can add them directly as a page resource (but you’ll have to type in a longer file path, since they’re not Style Sheet design elements — see Russ’ sample database for an example) or include them application-wide in a Theme.

<resources>
  <styleSheet href="font-awesome-4.0.3/css/font-awesome.css"></styleSheet>
</resources>

When going to production, use font-awesome.min.css instead of font-awesome.css.

If you’re using a version other than 4.0.3, update the file path accordingly to match the folder name under WebContent.

Common Error

To try it out, I added an icon tag to an XPage:

<i class="fa fa-users"></i>

In Firefox and Chrome, it doesn’t work by default. (Surprisingly, it does seem to work at this point in IE9+. There are special stylesheets to use font-awesome in IE8 and IE7, but that’s outside of the scope of this post.)

Here’s what it looks like in Chrome, along with the errors in the console (click the image to see it larger):

FontAwesome_Error_Chrome

Here’s what it looks like in Firefox, along with the errors in the console (click the image to see it larger):

FontAwesome_Error_Firefox

They’re both showing “Bad Request” errors. This has to do with URL parameters included for the font references in the Font Awesome stylesheets.

Fixing the Links

The ?v=4.0.3 appended to each font reference is what’s causing the problem. The server does not seem to like those parameters.

To allow it to work within XPages, open up the Font Awesome stylesheet (via Package Explorer or Navigator) and update the font links. You can find the lines to fix at the top of the stylesheets, in the @font-face { } rules.

@font-face {
  font-family: 'FontAwesome';
  src: url('../fonts/fontawesome-webfont.eot?v=4.0.3');
  src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');

All you have to do is remove the ?v=4.0.3 from each url to fix the problem.

In the links that include anchor links (such as: ?#iefix&v=4.0.3), just remove the 4.0.3 part and you can leave the anchor link.

@font-face {
  font-family: 'FontAwesome';
  src: url('../fonts/fontawesome-webfont.eot');
  src: url('../fonts/fontawesome-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff') format('woff'), url('../fonts/fontawesome-webfont.ttf') format('truetype'), url('../fonts/fontawesome-webfont.svg#fontawesomeregular') format('svg');

(Make the change in both font-awesome.css and font-awesome.min.css.)

Now my sample page shows the “Users” icon.

FontAwesome_Icon


Using Font Awesome Icons in XPages

$
0
0

In the last post, I showed how to implement Font Awesome in your XPages application. In this post, I’ll show two ways to display Font Awesome icons on your pages.

Option 1 – Pass-through HTML

Font Awesome icons are not XPages controls, so you can add them to your page directly with pass-thru HTML, an <i> tag. The Font Awesome CSS looks for this tag and replaces it with the correct icon, based on the class name that you provide.

Icon tags generally take this form:

<i class="fa fa-XXX"></i>

Here’s an example of the crosshairs icon:

<i class="fa fa-crosshairs"></i>

FontAwesome_2_Icon

This page contains all icons and their corresponding class names.

Option 2 – Computed Text

Another option you have for adding an icon within XPages is a computed text control.

Since it’s an xp control, you can do more with it, such as conditionally rendering the icon or conditionally computing the class name to change which icon is displayed.

One way to use a computed text control would be to set the Content type property to HTML (which sets escape="false" in the source) and use JavaScript to compute the HTML output.

Here’s an example that will generate the same icon shown above:

<xp:text escape="false" id="computedField1">
  <xp:this.value>
    <![CDATA[#{javascript:return '<i class="fa fa-crosshairs"></i>';}]]>
  </xp:this.value>
</xp:text>

This adds the icon, but also puts a <span> tag around it (as the Computed Field control always does by default).

Here’s the HTML output:

<span id="view:_id1:computedField1" class="xspTextComputedField">
  <i class="fa fa-crosshairs"></i>
</span>

A better method is to use the tagName property of the Computed Field control (as I described in this post).

When you do this, you don’t have to compute any value for the control — just set the tagName to i and set the Class to define the icon that you want. You also don’t have to set the Content type to HTML.

Here’s what it looks like in the XPages source:

<xp:text escape="true" id="computedField2"
  styleClass="fa fa-crosshairs" tagName="i">
</xp:text>

This generates only the HTML that you really want for the icon, without the extraneous <span> tag mixed in. Most of the time it’s probably not a big deal, but it has the potential affect your UI.

<i class="fa fa-crosshairs"></i>

More on Using Font Awesome

You can change the size, orientation, and color of Font Awesome icons (and much more!). You can even stack multiple icons (but not in older IE) to create unique images.

Check out this post by Russ Maher or the Font Awesome site for more information.

Up Next

In the next post, I’ll show how to add an event handler to a Font Awesome icon in XPages.


Adding Event Handlers to Font Awesome Icons in XPages

$
0
0

In the previous two posts, I showed how to implement Font Awesome in XPages and two options for adding icons to your page. In this post, I’ll show how you can add event handlers so that you can trigger an action when a user clicks on an icon.

Add a Client-Side Event Handler with Dojo or jQuery

If you want to trigger a client-side event, you can use Dojo or jQuery to add an event handler to the icon after the page loads.

For example, if you have this bullseye icon…

<i id="myIcon" class="fa fa-bullseye"></i>

…and you want to add an event handler to display an alert when the user clicks on it, you can use this Dojo code:

dojo.on(dojo.byId('myIcon'), 'click', function() {
  console.log('clicked icon');
});

Here’s more information about attaching event handlers (you may need to include the dojo.on module) and event delegation in Dojo.

Add Icon Within an XPages Control

Another option for making icons clickable is to put them inside of another XPages control that has an event handler. For example, you can create a link control, button control, span control, etc and put a font awesome icon inside of it.

Here’s an example of displaying an icon within an XPages button. It displays a message in the console with client-side JavaScript and also triggers a server-side partial refresh of a panel.

To add the icon, you have to switch to the source view and add it directly.

<xp:button value=" Click Me" id="button1">
  <i id="myIconButton" class="fa fa-bullseye"></i>
  <xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="myPanel">
    <xp:this.script><![CDATA[console.log('clicked icon button');]]></xp:this.script>
  </xp:eventHandler>
</xp:button>

FontAwesomeButton

Notice that it turns the icon blue by default. It does the same when inside of a link, because the browser makes link text blue by default (and these icons are implemented as a font, so therefore, the browser is correctly treating them as text).

Here’s an example of displaying an icon (with the same effect) within an XPages link:

<xp:link escape="true" text="" id="link1">
  <i id="myIconLink" class="fa fa-bullseye"></i>
  <xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="myPanel">
    <xp:this.script><![CDATA[console.log('clicked icon link');]]></xp:this.script>
  </xp:eventHandler>
</xp:link>

Add an XPages Event Handler Directly to an Icon

In my last post, I mentioned that I prefer to use computed text fields with the tagName property set, because you can set a rendered formula and compute the class names as needed. Another advantage to that approach is that you can attach an XPages event handler, so you can add server side code, partial refreshes, onComplete callbacks, etc.

Now, that comes with a significant caveat, because Computed Field controls do not provide the ability for you to add events in the “pretty panels”.

However, if you add an event handler to another control, you can go into the Source and move that event handler into the Computed Field (xp:text) control and it will fire.

In this example, I created a Computed Field to display an icon and I copied the event handler from the previous example into it.

<xp:text escape="true" id="computedField2" tagName="i"
  styleClass="fa fa-bullseye fa-3x">
  <xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="myPanel">
    <xp:this.script><![CDATA[console.log('clicked icon link');]]></xp:this.script>
  </xp:eventHandler>		
</xp:text>

If you go into the source and click on the xp:eventHandler tag, you’ll get a property panel where you can bring up the client or server JavaScript editor or add other callbacks.

FontAwesomeEventHandler

Now, I have a clickable icon with no extraneous tags around it.

It is certainly simpler to wrap an xp:span tag around an icon to get the same effect, but if you need the clickable icon without additional tags, this provides a way to do it.

Tip – Changing the Cursor when Adding an Event Handler to an Icon

If you’re going to make standalone icons clickable, the text cursor may be confusing (at least in IE and Firefox). (Remember — it’s actually a font, so this makes sense).

If you want to change the cursor when hovering over an icon, you can add this CSS:

i.fa {
  cursor:pointer;	
}

Of course, this will affect the cursor over all font awesome icons in your application. You can target the CSS as needed to only modify it for icons that are clickable.


Improving the Responsivness of an XPage/Custom Control with a View Panel in Domino Designer

$
0
0

In his presentation during the recent TLCC webinar, Mark Roden had some great tips on speeding up Domino Designer. During the open Q&A period, a question about improving the responsivness of DDE with a page containing a view panel was raised. In this post, I’ll provide a few tips to help.

The Problem

There are times when there is a significant delay in Domino Designer (DDE) when attempting to view or modify properties of a View Panel control. Even in the source view, you may change a property and wait several seconds (or more) between each keystroke. Just clicking on the panel or a column or a subtab in the properties view can be very slow. With every click or keystroke DDE appears to be resolving the data source. The lag can become extremely frustrating, based on machine, server, and network performance. The constant delays can be a significant detriment to your ability to be productive.

Fortunately, there are a few things you can do to improve the situation.

Ensure that DDE Memory Settings are Correct

This tip isn’t view-specific, but for DDE performance in general.

When you install Domino Designer, the default memory settings are far too low. See this TLCC tip for information on how to provide more memory to Domino Designer, thus dramatically speeding up its performance.

Even if you fix the settings, they will be reverted when you upgrade to a newer version of Domino Designer.

Work Locally

The lag in working with a view panel is usually far, far, far (did I say “far”?) worse when working over a network on the server. If you can replicate the application locally for your development and replicate frequently to the server for testing, you will generally see a significant improvement in the responsiveness of Domino Designer when working with the view panel.

Compute the Data Source

If you cannot work on a local replica (or if that doesn’t speed up performance enough), you can compute the view’s data source.

When the data source is computed, Domino Designer no longer attempts to resolve it and read information every time you click on any part of the panel or use any keystroke to modify a property.

Originally, the view data source will look something like this:

<xp:this.data>
  <xp:dominoView var="view1" viewName="myViewName>
  </xp:dominoView>
</xp:this.data>

When you compute the source, it looks more like this:

<xp:this.data>
  <xp:dominoView var="view1"> 
    <xp:this.viewName>
      <![CDATA[#{javascript:return "myViewName";}]]>
    </xp:this.viewName>
  </xp:dominoView>
</xp:this.data>

This provides a massive improvement in responsiveness — even when working on a remote server.

However, since the data source is not being resolved by Domino Designer, you will lose column selection drop-downs.

My recommendation is to implement the view panel and drag-and-drop the columns that you’d like in the view, then change the data source to be computed and modify formatting or other view properties after that.

Note: If you’re pointing to a different server or different database name, compute both of those properties as well to gain this performance benefit.


Dojo in XPages – 19: Using AJAX to Retrieve Data from a REST Service

$
0
0

AJAX is a critical component to creating applications that are highly responsive (performance-wise) in the browser. When you initiate an asynchronous request, the rest of the page is not blocked while some server action is executed. AJAX wrapper classes are built into the Dojo base module, so they’re automatically available to use in XPages. In this post, I’ll show how you can make an AJAX call with Dojo to retrieve data from a REST service in XPages.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

The Test Data and REST Service

I created some test documents that have FirstName, LastName, and Address fields. I created an XPage named REST.xsp and added a REST service (viewJsonService) to provide the data. The pathInfo property of the REST service is set to People, so the URL to reference it from within the same application is REST.xsp/People.

Here is the source of the REST service:

<xe:restService id="restService1" pathInfo="People">
  <xe:this.service>
    <xe:viewJsonService defaultColumns="true"
      viewName="People">
    </xe:viewJsonService>
  </xe:this.service>
</xe:restService>

Here’s what the data looks like when I display it in the browser:

Dojo 19 - A - REST Data

Tip: Remember to always test your REST service output in the browser so you can verify that it’s returning data that you expect.

Retrieving the Data with AJAX

To retrieve that data from another XPage, I can use this dojo code:

// Retrieve data from a REST service and write it out to the console
dojo.xhr.get({
  url:"REST.xsp/People",
  handleAs:"json",
  load: function(data){
    for(var i in data){
      console.log("#" + i + ": value", data[i]);
    }
  }
});

XHR is short for XMLHttpRequest — the object that’s used to make an asynchronous request. xhr.get() is the Dojo wrapper function for making that asynchronous call from client-side JavaScript.

The url parameter refers to my page with the REST service (including the pathInfo property that refers to a specific REST service). This can be any URL that you need.

The load property is a callback function that runs after data has been retrieved. It’s an anonymous function that automatically receives a handle to the data that was retrieved. The parameter name can be whatever you want.

Since the JSON data returned by the REST service is an array of objects, this code loops through the array and prints the retrieved data to the console.
Dojo 19 - B - REST Data

And there you have it – an asynchronous call that retrieves data and writes it out to the console.

In the next post, I’ll dig a little more into the callbacks and demonstrate displaying the returned data on the page.


Dojo in XPages – 20: Handling Successful or Failed AJAX Requests

$
0
0

In the last post, I showed how to use an xhr.get() request in Dojo to asynchronously retrieve information from a REST service. In this post, we’ll look at how to execute code when the request is completed or returns an error within an example that demonstrates how to display the response (or an error message) on the page.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

XHR Callbacks

The Dojo xhr requests provide the opportunity to define up to 3 callback functions that run after the request is completed. The xhr request is generally asynchronous (although there’s a parameter to change that), so it doesn’t block the user from interacting with the rest of the page while data is retrieved. The callback functions are useful in that they can see the results of the request and respond accordingly. All callbacks are anonymous javascript functions that automatically receive at least one parameter.

load – the ‘success’ callback

As shown in the last post, the load callback is executed when the request is completed and data has been retrieved successfully. The function automatically receives a parameter that contains the data that was returned by the request, in the format specified by the handleAs parameter (generally text, json, or xml).

error – the ‘failure’ callback

The error callback is executed when the request cannot be completed successfully. The function automatically receives a parameter defining the error and can also use a second parameter to determine the HTTP status code (among other things).

handle – the ‘always’ callback

In addition to load and error, there is another callback option called handle. This callback runs regardless of whether the call was successful or failed. You can review the response and determine whether it was successful based on the data. This is an option if you want to have some code run regardless of the response and you don’t want to duplicate it in both the load and error callbacks.

An Example

Building on the example shown in the last post, this code will request data from a REST service. If successful, it will write the data onto the page in the form of a table (assuming there’s a passthru HTML div on the page with the ID “resultsDiv”). If it fails, it will display a message in that div in red.

dojo.xhr.get({
  url:"REST.xsp/People",
  handleAs:"json",
  load: function(data){
    var output = '<table>';

    // Build the header row by inspecting column names
    output += '<tr>';
    for (var col in data[0]) {
        output += '<th>' + col + '</th>';
    }
    output += '</tr>';

    // Build the data rows
    for(var i in data){
      output += '<tr>';
      for (col in data[i]) {
        output += '<td>' + data[i][col] + '</td>';
      }
      output += '</tr>';
    }
    output += '</table>';
    dojo.byId('resultsDiv').innerHTML = output;
  },
  error: function(msg, args) {
    dojo.style('resultsDiv', 'color', 'red');
    dojo.byId('resultsDiv').innerHTML = 'Status: ' + args.xhr.status + '<br />' + msg;
  }
});

When successful, in inspects the structure of the data returned and builds a table to display it:

Dojo 20 - A - Display REST Data

To test out the error code, just point it at a page that doesn’t exist.

Dojo 20 - B - Display REST Error


XPages Tip – Disable AutoComplete on a Field

$
0
0

Browsers will often remember values that you enter in fields and provide typeahead/autocomplete functionality for you when using fields with those same names in the future. This is also true of fields on your XPages forms by default. In this post, I’ll show you how to disable this on an input text control.

Disabling autocomplete is a very simple change to make. Just select your input control, go to All Properties and set the autocomplete property (under basics) to off.

AutoComplete1

To test the property, I created a simple page that has two fields bound to a form. I left the first field alone, but I set autocomplete to off on the second field. I submitted the form a number of times, entering ‘Name1′ into both fields, then ‘Name2′, and so on.

After several rounds of this, when I type an ‘N’ into the first field, I got a typeahead list of all of the values that I had entered that began with the letter ‘N’.

AutoComplete2

However, in the second field, even though I entered all of the same values as the first field, there was no typeahead.

AutoComplete3



XPages Tip – Disable Autocomplete on All Fields via the Theme

$
0
0

In my last post, I showed how to disable autocomplete on a single field. In this post, I’ll show how to disable it application-wide via the theme.

If you want to prevent autocomplete on all fields in your application, you can do it in one fell swoop by adding this code to your theme:

<!-- Set all edit box controls to disable autocomplete -->
<control>
  <name>InputField.EditBox</name>
  <property mode="override">
    <name>autocomplete</name>
    <value>off</value>
  </property>
</control>

This will automatically insert the autocomplete="off" attribute into all Edit Box controls in the application.

Here’s an example of what a field now looks like in the browser page source:

<input type="text" id="view:_id1:AutoComplete" name="view:_id1:AutoComplete" autocomplete="off" class="xspInputFieldEditBox"><br>

Dojo in XPages – 21: Displaying Images with a ThumbnailPicker

$
0
0

If you have images attached to a document in an XPages application, you can display a scrolling series of them with the ThumbnailPicker widget. In this post, I’ll show how to include the required dojo modules and dynamically populate the picker with a list of images.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

ThumbnailPicker Widget

The ThumbnailPicker widget can display images horizontally or vertically and it provides controls to page through the images. If you have a document with multiple image attachments, it can be a handy way to display thumbnails of them.

Here’s an example of each orientation from my test page.

Dojo21_1

Including Required Dojo Resources

The ThumbnailPicker widget is part of dojox; it’s already on the Domino server and can be used on the page by adding a few resources and a div to render it.

There are two dojo modules and one dojo stylesheet required in order to make this work.

You need the ThumbnailPicker module, the ItemFileReadStore module (to supply the list of images to the widget), and a stylesheet to display it properly.

<xp:this.resources>
  <xp:dojoModule name="dojox.image.ThumbnailPicker"></xp:dojoModule>
  <xp:dojoModule name="dojo.data.ItemFileReadStore"></xp:dojoModule>
  <xp:styleSheet href="/.ibmxspres/dojoroot/dojox/image/resources/image.css"></xp:styleSheet>
</xp:this.resources>

Providing Data

The ThumbnailPicker requires an ItemFileReadStore to provide the image information.

The structure generally looks like this:

imageItemStore.data = {
  identifier: 'title',
  label: 'Images',
  items: [
    {
      thumb: "http://server.com/mypicture.jpg",
      title: "title 1"
    },
    {
      thumb: "http://server.com/mypicture2.jpg",
      title: "title 2"
    }
  ]
};

In order to build this based on attachments on the current document, we want to build that list of items dynamically.

Each image url needs to be built in this format: DB.nsf/0/[docUNID]/$File/[attachmentname]

The code below will build that structure based on the files attached to the current document. It can run in the page’s onClientLoad event.

Line 2 gets the list of attachment names with @AttachmentNames().

Line 4 parses it to be an array, because it is returned to client-side javascript as a string surrounded in square brackets, with each value separated by a comma and a space. [file1.jpg, file2.jpg, file3.jpg]

Lines 7 and 8 set up objects that are required by the data store for the ThumbnailPicker. The itemNameMap object defines the property that points to the image “thumbnail” (in quotes because we’re not really using a thumbnail — we’ll just display the actual attachment as a thumbnail). The request object doesn’t need any parameters in this case. That’s used when making remote requests.

As shown previously, the data store needs an items property that is an array of objects (each with a thumb property and a title property). Lines 11 – 19 build array of objects for the data store.

Lines 22-26 set up the actual ItemFileReadStore object and provide it the data. The identifer property tells it which data property to use for the image title (which you see when you hover your mouse over the image).

Line 29 finds the div with the id thumbnailDisplay and connects the data store to it in order to initialize it.

// Get an array of attachment names.
var fileNames =	"#{javascript:@AttachmentNames();}";
// It comes back as a concatenated string, surrounded by square brackets, to parse into an array
fileNames = fileNames.substring(1, fileNames.length-1).split(', ');

var docID = "#{javascript:currentDocument.getDocument().getUniversalID()}";
var itemNameMap = {imageThumbAttr: "thumb"};
var request= {};

// Build an array of image items for the thumbnail's data store
var imageData = {};
imageData.items = [];
for (var i=0; i< fileNames.length; i++) {
  var newItem = {};
  newItem.thumb = './0/' + docID + '/$File/' + fileNames[i];
  newItem.title = 'Image: ' + fileNames[i];
  
  imageData.items.push(newItem);
}

// Initialize the data store
var imageStore = new dojo.data.ItemFileReadStore({
  identifier: 'title',
  label: 'Images',
  data: imageData
});

// Initialize the Thumbnail Picker 
dijit.byId('thumbnailDisplay').setDataStore(imageStore, request, itemNameMap);

Rendering the ThumbnailPicker

Here’s how you can set up a passthru HTML div tag to become the ThumbnailPicker.

<div id="thumbnailDisplay" dojoType="dojox.image.ThumbnailPicker"
  size="500" data-dojo-props="isHorizontal:true">
</div>

The key is the dojoType attribute. When the dojo parser runs, it looks for these dojoType attributes and generates dijits. So, if you use this method, you’ll need to enable the page’s Trigger Dojo parse on load property to ensure that the parser runs when the page loads. (Otherwise, it won’t generate the dijit.)

Dojo21_2

As you can see in the page source above, this adds the attribute dojoParseOnLoad="true" to the xp:view tag.

Now, you’ve got what you need to generate the ThumbnailPicker.

Another option for Rendering the ThumbnailPicker

In addition to passthru HTML, we can also render the ThumbnailPicker with an xp:div tag.

You can achieve the same output by setting the dojo type to dojox.image.ThumbnailPicker and adding attributes for the size, etc. (Click on the picture to enlarge it.)

Dojo21_3

After you do that, you would just need to change the last line of the onClientLoad code to retrieve the XPages-generated client-side ID for the div.

dijit.byId('#{id:thumbnailDisplay}').setDataStore(imageStore, request, itemNameMap);

ThumbnailPicker Size

The size attribute generally defines the width (or height, if vertical) of the picker.

There’s also a numberThumbs attribute that’s supposed to define the number of images to display, but I did not have much success in getting it to take effect.

Resizing the Images

By default, this will dispay the images in their original sizes. Fortunately, with some CSS, you can resize them to make them consistent.

For my example, I created another stylesheet and included it as a page resource, based on an example found in the Dojo documentation.

.thumbWrapper img {
  height: 100px;
  max-width: 75px;
}
.thumbOuter.thumbHoriz, .thumbHoriz .thumbScroller {
  height: 100px;
}

Displaying the ThumbnailPicker Vertically

In the div tag shown above, there’s a data-dojo-props attribute with a value of "isHorizontal:true". That property isn’t required, as that’s the default, but it shows how you can easily change the orientation of the display. If you set that property to false, it will display the picker vertically.


Dojo in XPages – 22: Displaying an Image Gallery of Attachments

$
0
0

In the last post, I showed how to display a scrolling ThumbnailPicker of images attached to a document in XPages. In this post, we’ll look the Gallery widget, which displays thumbnails but also displays a larger version of the selected image.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Gallery

The Gallery widget combines features of the Dojo ThumbnailPicker and Slideshow widgets. It displays a list of thumbnails, has an area to display a larger version of the selected image, and displays the image title (along with a counter). (Click to see the image larger.)

Dojo 22 - A

You can scroll through the thumbnails and click on a thumbnail to display a larger version of it after fading it in.

When you hover over the larger image area, you see additional controls. You’ll have a “left” arrow if you’re past the first image. You’ll also have a “Play” icon (right arrow) on the right. When clicked, it will start a slide show that will automatically scroll through the images. You can hover again and click the “stop” icon to end the slide show and remain on the current image. (These controls are only visible when hovering over the larger image area; the screen capture does not show the cursor.)

Dojo 22 - B

This is a very similar setup to the ThumbnailPicker — I actually implemented it just by changing the dojo module, changing the div tags’ dojoType, and adding the imageLargeAttr attribute to the image map and to each image object in the data store. However, in order to allow this to stand alone, I’ll show all of the code required to set it up. (For more of an explanation on the code, refer to the previous post.)

Step 1 – Include Dojo Modules and Stylesheet

There are two dojo modules and one dojo stylesheet that need to be included in order for it to work:

<xp:this.resources>
  <xp:dojoModule name="dojo.data.ItemFileReadStore"></xp:dojoModule>
  <xp:dojoModule name="dojox.image.Gallery"></xp:dojoModule>
  <xp:styleSheet href="/.ibmxspres/dojoroot/dojox/image/resources/image.css"></xp:styleSheet>
</xp:this.resources>

Step 2- Add a div to render the gallery

Just add a div tag, give it an id to reference and set the dojoType to dojox.image.Gallery.

<div id="galleryDisplay" dojoType="dojox.image.Gallery"
  data-dojo-props="imageHeight:400, imageWidth:600">
</div>

Step 3 – Set the page to parse dojo on load

This property runs the Dojo parser when the page loads, so it will find the div with the dojoType attribute and generate the dijit.

Dojo21_2

Step 4 – Build the image data store and attach it to the gallery

In order to build this based on attachments on the current document, we want to build that list of items dynamically.

Each image url needs to be built in this format: DB.nsf/0/[docUNID]/$File/[attachmentname]

The code below will build that structure based on the files attached to the current document. It can run in the page’s onClientLoad event.

Line 1 gets the list of attachment names with @AttachmentNames().

Line 3 parses it to be an array, because it is returned to client-side javascript as a string surrounded in square brackets, with each value separated by a comma and a space. [file1.jpg, file2.jpg, file3.jpg]

Lines 6-9 set up objects that are required by the data store for the Gallery. The itemNameMap object defines the property that points to the large image and the “thumbnail” (in quotes because we’re not really using a thumbnail — we’ll just display the actual attachment as a thumbnail).

As shown previously, the data store needs an items property that is an array of objects (each with a large property, a thumb property and a title property). Lines 12-21 build array of objects for the data store.

Lines 23-27 set up the actual ItemFileReadStore object and provide it the data. The identifer property tells it which data property to use for the image title.

Line 32 finds the div with the id galleryDisplayand connects the data store to it in order to initialize it.

var fileNames =	"#{javascript:@AttachmentNames();}";
// It comes back as a concatenated string, surrounded by square brackets, to parse into an array
fileNames = fileNames.substring(1, fileNames.length-1).split(', ');
var docID = "#{javascript:currentDocument.getDocument().getUniversalID()}";

var itemNameMap = {
  imageThumbAttr: "thumb",
  imageLargeAttr: "large"
};

// Build an array of image items for the thumbnail's data store
var imageData = {};
imageData.items = [];
for (var i=0; i< fileNames.length; i++) {
  var newItem = {};
  newItem.thumb = './0/' + docID + '/$File/' + fileNames[i];
  newItem.large = './0/' + docID + '/$File/' + fileNames[i];
  newItem.title = 'Image: ' + fileNames[i];

  imageData.items.push(newItem);
}

// Initialize the data store
var imageStore = new dojo.data.ItemFileReadStore({
  identifier: 'title',
  label: 'Images',
  data: imageData
});

var request= {}; 

// Initialize the Gallery 
dijit.byId('galleryDisplay').setDataStore(imageStore, request, itemNameMap);

If you use an xp:div rather than a passthru HTML div, then the last line will change to this:

dijit.byId('#{id:galleryDisplay}').setDataStore(imageStore, request, itemNameMap);

Changing the Slideshow Interval

You can modify the length of time between automatic advancement of pictures in the slideshow by adding a slideshowInterval property to the data-dojo-props attribute and specifying the number of seconds. There’s only one data-dojo-props attribute — if you already have a property defined, just add the additional property to the comma-separated list.

Here’s an example of the gallery div, with image and interval settings:

<div id="galleryDisplay" dojoType="dojox.image.Gallery"
  data-dojo-props="imageHeight:250, imageWidth:500, slideshowInterval:2">
</div>

Thumbnail Image Size

As shown in the previous post, you can use CSS to define the image size for the thumbnails if you’d like to change the defaults (75px high by 100px max width).

Other Data Stores

The focus of this post was how to use the gallery to display images attached to a document in XPages, but there are also data stores to work with Flickr and Picasa directly. (If you use those with large sets of images, you’ll want to update the request object to specify the count of images per request and, optionally, the starting point.)

https://dojotoolkit.org/reference-guide/1.8/dojox/image/Gallery.html#id4


Dojo in XPages – 23: Using a Lightbox to Display Image Attachments

$
0
0

In recent posts, I showed how to create a thumbnail picker and a gallery of attached images on an XPage. In this post, I’ll show how to use the Lightbox widget to display images in a specialized dialog that provides a the ability to cycle through the images.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

Lightbox

The Lightbox widget provides a nice way to focus on an image and display it centered on the screen in a dialog. Whatever portion of the page is unused (vertically) is darkened with an underlay so it stands out.

It is possible to create a link to open an individual image as in a Lightbox, but, in this post, I’m going to focus on how to create a group of images (based on attachments to the current document) in order to create more of a slideshow effect where you can click through the images.

Dojo23_A

The Lightbox displays one image at a time, but, when there is a group of images assigned to it, it also displays controls to move left and right through the images. There is also a button to close the dialog.

Additionally, the up and left arrow keys go back and the down and right arrow keys work go forward. The ESC key closes the dialog.

Generating the Lightbox only requires two steps — including required resources and programmatically creating the Lightbox and providing the list of images.

1) Include Required Resources

The dojox.image.Lightbox module is required, along with a corresponding stylesheet.

<xp:this.resources>
  <xp:dojoModule name="dojox.image.Lightbox"></xp:dojoModule>
  <xp:styleSheet href="/.ibmxspres/dojoroot/dojox/image/resources/Lightbox.css"></xp:styleSheet>
</xp:this.resources>

2) Creating the Lightbox and Providing Images

This code will automatically generate and display the Lightbox based on images attached to the page. Use the code in the onClientLoad event to render the Lightbox immediately or on a button or other client-side event to render it on demand.

Lines 2 and 4 retrieve the names of the file attachments on the current document and put them into an array.

Lines 8-9 programmatically create the Lightbox and initialize it.

Lines 12-14 cycle through the attachment names and add each image to the Lightbox dialog

// Add each attached image to the lightbox
// Build each URL in this format: DB.nsf.nsf/0/docUNID/$File/attachmentname
var fileNames =	"#{javascript:@AttachmentNames();}";
// It comes back as a concatenated string, surrounded by square brackets, to parse into an array
fileNames = fileNames.substring(1, fileNames.length-1).split(', ');
var docID = "#{javascript:currentDocument.getDocument().getUniversalID()}";

// Initialize the Lightbox dialog 
var lbDialog = new dojox.image.LightboxDialog({});
lbDialog.startup();

// Add images to a group
for (var i=0; i< fileNames.length; i++) {
  lbDialog.addImage({ title:'Image: ' + fileNames[i], href:'./0/' + docID + '/$File/' + fileNames[i]}, 'imgGroup');
}

// Display the Lightbox, starting with the first image
lbDialog.show({group:"imgGroup", title:fileNames[0]}); 

Note: if you don’t specify the url for the first image to display, it’ll start with the first one, but display ‘undefined’ for the title. Line 17 passes in the first image title to avoid this.

Automatically Cycling Through the Images

If desired, you can easily add a timer to automatically cycle through the images, using the _nextImage() or _previousImage() methods of the Lightbox.

setInterval(function(){
  lbDialog._nextImage();
}, 3000);

This example works if added right after the code that creates the Lightbox. If you trigger this separately, then replace line 2 with something like this:

dijit.byId("dojoxLightboxDialog")._nextImage();

You’d also have to give the Lightbox widget an ID so you could refer to it this way.

Changing the Color of the Underlay

By default, it will darken the unused area at the end of your page with a black underlay. (This can look odd if you have some content on the page, but not the rest of the page, as only part of the page will be darkened.)

If you want to change the color of the underlay, you can add your own stylesheet and use a rule like this:

.dijitDialogUnderlay._underlay {
  background-color:blue
}

Dojo in XPages – 24: Using a LightboxNano to Display an Attached Images

$
0
0

In the last post, I showed how to create a Lightbox dialog that allows you to cycle through a group of images. In this post, I’ll show how you can create a simpler version that displays a single image with the LightboxNano module.

Dojo in XPages Series

Dojo in XPages — All Blog Posts

LightboxNano

LightboxNano is a lightweight version of a lightbox, designed to display a single image and focus on it by providing an overlay on the rest of the screen.

Dojo24_E_Nano_FullSize

The control is designed to display a thumbnail and preload the larger image (after a few seconds) so it’s ready to display in full size. It automatically resizes the image as needed to ensure that it fits within the browser window.

From the point where you click on the thumbnail, it provides a nice animation to enlarge and center the full image, leaving a small dotted outline in place of the thumbnail. Clicking anywhere on the page will close the image, providing the reverse animation to shrink the image and move it back to the spot of the original link.

Here are a few screen captures of the animation.

Dojo24_B_Nano_Opening

Dojo24_C_Nano_Opening

Dojo24_D_Nano_Opening

In this example, I’ll show how to create a LightboxNano from the first image attached to a document. If you have large images, you can modify it to use a thumbnail version and load the full version asynchronously.

1) Include the Required Dojo Module

All you need is the dojox.image.LightboxNano module

<xp:this.resources>
  <xp:dojoModule name="dojox.image.LightboxNano"></xp:dojoModule>
</xp:this.resources>

2) Add a thumbnail image to the page

Use an Image control to define the path to the image. This example computes the image path for the first file attachment.

<xp:image id="lbNanoImage" style="height:50px;width:50px">
  <xp:this.url>
    <![CDATA[#{javascript:return './0/' + currentDocument.getDocument().getUniversalID() + '/$File/' + @AttachmentNames()[0];}]]>
  </xp:this.url>
</xp:image>

I used inline styles to size the image; otherwise it would display full-sized.

3) Create the Lightbox

With a few lines of code, you can build the image file path with client-side JavaScript and instantiate the LightboxNano.

This code should run on the onClientLoad event of the page.

// Build the URL for the first attachment in this format: DB.nsf.nsf/0/docUNID/$File/attachmentname
var fileName =	"#{javascript:@AttachmentNames()[0];}";
var docID = "#{javascript:currentDocument.getDocument().getUniversalID()}";

// Initialize the LightboxNano and provide the full URL and the id of the image tag displaying the thumbnail
new dojox.image.LightboxNano({
  href: './0/' + docID + '/$File/' + fileName
}, "#{id:lbNanoImage}");

Since this example creates the LightboxNano programmatically (rather than declaratively), you do not need to set the pages parseOnLoad property to automatically render the LightboxNano.


Viewing all 216 articles
Browse latest View live