JavaFX from the Trenches – JFXtras Spinner Controls

Up until now I have been happy to use the standard set of JavaFX controls in Evidentia, my commercial product written in JavaFX. However I have an enhancement in the works for which I wanted a little something extra, and a spinner control made sense.
spinnershot1

Enter JFXtras.

I basically wanted to present the user the ability to choose fields from the spinners to populate the fields on the left. Spinners made sense to me since there could be a lot of trial and error on the users part, and spinners were faster than drop down lists for experimenting.

JFXtras is a sort of laboratory for controls that haven’t made it into JavaFX (yet?) or which are beyond what one would expect in a basic framework. There are some really cool stuff in the ‘toolbox’, but being unofficial means minimal documentation, and some features you might expect in a standard control may or may not be present.

The Form

If you start up Ensemble, the JFXtras demo application, you can view the suite of controls available. It also provides sample code for using the controls. I’ll just show you the spinners:
Ensemble_spinners

I downloaded the library and added it to my project in Netbeans easy enough. I have gotten used to using FXML, so I had to adjust back to the traditional programming model, which was no big deal. I added a spinner to a grid…

        srcRefNoSpinner = new ListSpinner<>(srcRefList)
                               .withCyclic(true)
                               .withArrowDirection(ListSpinner.ArrowDirection.HORIZONTAL)
                               .withArrowPosition(ListSpinner.ArrowPosition.SPLIT)
                               .withAlignment(Pos.CENTER)
                               ;
        GridPane.setConstraints(srcRefNoSpinner,4,1);

…where srcRefList referenced an ObservableList.

The first issue I had was that the meaningful part of the control was not vertically centered.spinnershot2

This is when I realized how dependent I had become on SceneBuilder to sort out these kind of issues. I searched around for styling instructions and was encouraged to join the JFXtras mailing list, which I did.

Still, couldn’t find much information, and I never did really resolve it. I finally addressed the issue by managing the height of the control, which was ultimately what I was going to want to do anyway.

        srcRefNoSpinner = new ListSpinner<>(srcRefList)
                               .withCyclic(true)
                               .withArrowDirection(ListSpinner.ArrowDirection.HORIZONTAL)
                               .withArrowPosition(ListSpinner.ArrowPosition.SPLIT)
                               .withAlignment(Pos.CENTER)
                               ;
        srcRefNoSpinner.setPrefHeight(20.0);
        srcRefNoSpinner.setMinHeight(20.0);
        srcRefNoSpinner.setMaxHeight(20.0);
        GridPane.setConstraints(srcRefNoSpinner,4,1);

The Function

I struggled a bit with how to attach a ChangeListener to the ListSpinner control. I kept looking for a selection model for the control. I eventually found I had to attach it to the value property:

        srcRefNoSpinner.valueProperty().addListener(new RefNoSpinnerListener());
...
        private class RefNoSpinnerListener implements ChangeListener {
 
            @Override
            public void changed(ObservableValue<? extends String> ov, String t, String t1) {
                String id = "";
                switch(t1) {
                    case "XREF":
...
                }
                srcRefNo.setText(id);
            }
        }

Pretty straightforward.

The last challenge was initializing the selected value of the spinner based on an action the user takes on another tab. Again, I looked for a selection model to work with, but the solution was actually simpler:

     srcRefNoSpinner.setValue("XREF");

Summary

All in all adding a spinner to my application was pretty straight forward, and functionally there have been no issues. Some of the issues I ran into were my own doing (I thought setValue() didn’t work, created a rather embarrassing post on the email list, only to discover I had put it on the wrong code path).

Styling is still a mystery, as there is no documentation, but I find styling of the standard JavaFX controls can be just as challenging.

I also am still scratching my head – should I have expected the control to have a selection model? if it had been part of the standard JavaFX control set would I have been right in expecting it to follow the selection model design? Was the lack of one just a victim of a control in a laboratory that hasn’t fully matured?

Either way I was grateful for the work someone had done to create this control, and the simplicity with which I was able to add it to my application.

Hope this helps!

Posted in JavaFX | 3 Comments

JavaFX from the Trenches – Singleton Controllers

My genealogy application, Evidentia, uses a LOT of stages. Those stages are REUSED a LOT of times. In order to reuse the stages in the most efficient way possible (memory and CPU wise), I implemented the good ole Singleton design pattern.

Now this isn’t glamorous, but then “From the Trenches” is about the day to day stuff in JavaFX, so I wanted to share how I chose to implement the pattern at the controller level.

So what were my “requirements”?

  • Needed to reuse the same stages MANY times but with different data initializing the various fields.
  • Needed to reuse the same stages from DIFFERENT parent windows.
  • If the stage had dynamic controls (controls that could appear/disappear depending on how the user utilized it), the stage had to reinitialize itself to a known state, either after use or before reuse.

Pretty typical Java stuff. Here is the boiler plate for almost ALL my controllers:

public class LicenseInstallerController implements Initializable {
 
// define various @FXML fields to be initialized
 
    private static Stage myStage = null;
    private static LicenseInstallerController instance = null;
 
    public static synchronized void show(MyDataObject myData) throws IOException {
        if (myStage == null) {
	    // things that only need to be done once
	    // notice the reference to the Stage "myStage" is saved in a static variable
            URL location = LicenseInstallerController.class
                   .getResource("/org/ed4becky/evidentia/view/license/LicenseInstaller.fxml");
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(location);
            Scene scene = new Scene((Pane) loader.load(location.openStream()));
 
	    // by now, I can count on the initiialize() method having been called
	    // and a reference to this controller being storedd in "instance" variable
            myStage = new Stage();
            myStage.setScene(scene);
            myStage.setTitle("Evidentia License Installer");
            myStage.initModality(Modality.NONE);
            myStage.getIcons().add(ImageCache.getInstance().get("Evidentia48x48.png"));
 
	    // by setting an owner (this case my MASTER" stage), two things happen
	    // 1) shutdown of app closes window, 2) toFront() works as expected
	    myStage.initOwner(EvidentiaController.myStage.getScene().getWindow());
        }
	// HERE is where I put code to return the state of the stage 
        // and its controls to a known state.
	// if you don't clear out text fields, selection indices, etc... 
        // there will be garbage left from the last time show() was called...
	...
	// anything specific to THIS call to show, like initializing text fields with data 
        // from "myData" is done here, just before myStage.show() is called
	...
        myStage.show();
        myStage.toFront();
    }
 
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        instance = this;
		// one time initializations to controls happen here
		// remember this is called during the loader.load(),
		// so its only called the FIRST time
    }
 
	public static LicenseInstallerController getInstance() {
		// yeah I know, NULL check and synchronize are missing...
		// the getInstance() method is seldom used - usually only for
		// message passing between stages
		return instance;
	}
}

A couple of my own comments:

  • Yes I know, I haven’t protected the instantiation of the instance variable – but in my app I generally know there won’t be multiple threads creating the controller. (I should still go back and clean that up!)
  • I ran into an issue where a stage that had lost focus (not on top, was blurred out) still had popup tool tips displaying and forcing the background window to the top. This was resolved by a call to initOwner(), I assume setting a proper window hierarchy.
  • getInstance() is usually only used for message passing between parent/child windows
  • Why make myStage static? Sometimes I find it useful to add a method that allows parent/child windows to have direct access to certain controls (like status message labels, or progress bars). Other times I need access to the stage in a stop() method.

There are of course, multiple ways to accomplish the stated goals. This is just my (current) way.

Comments welcome…



Posted in JavaFX | 3 Comments

JavaFX from the Trenches – Part 2 – It’s Not Always About the Sex

Editorial Note: I wanted to be fair in writing this article, and it is not my goal to attack anyone personally. I contacted Jonathan Giles, tech lead for UI Components and let him know about the article ahead of time. He reviewed it for me and offered some insights without getting defensive. I am not claiming that Jonathan endorses the article, but I am grateful for his time and effort, and share his vision of a community moving JavaFX forward. -EAT

Just before Christmas I released a commercial app (Evidentia) for the PC and MAC. That’s right, PC and MAC AT THE SAME TIME!

However the process was NOT painless, and in this series of “from the Trenches” posts, I plan to document lessons learned in hopes that others may suffer less.

Let me repeat that I am a fan of JavaFX2. You can do some great things with it, and I LOVE being able to use style sheets. However, in this series I plan to be open, honest and direct including stating matter-of-factly that JavaFX2, or more accurately 2.2, was not as stable as I had expected a 2.2 version to be.
I could NOT have been successful without the help of the folks at the JavaFX2 Forum!

There are a lot of blog posts, twitters, and the like about the sexy side of JavaFX. You can do some great things with it, and on cutting edge hardware to boot. High end graphics, 3D graphics, shiny new controls – they all show well at a conference.

But for a commercial application written for those with minimal tech savvy, it’s the simple things that matter. I went into this project with a simple, not so sexy expectation: anything I could do with a web control or with Java Swing would have its counterpart in JavaFX2. I was 80% right, but I ran into some trouble along the way. I will use my “favorite” control, the ComboBox, as an example.

ComboBox

I started work on Evidentia in March of 2012, about the same time this blog post came out at JavFXperience: New in JavaFX2.1: ComboBox I was excited that JavaFX was well along, and noted without concern that the autocomplete feature was NOT included yet; no concern because I had implemented autocomplete on a control before, and it wasn’t critical for my first release anyway.

I have this one dialog that has become a real pain. The requirement is simple: collect and return a list of Subject/claim combinations for the calling window.
AttachSubject
Did I say simple? The user needs to be able to pick a subject and/or a claim type from a drop down list. If the name they want is not in the list, they need to be able to add it on the fly. Oh and if they add a name, it should be available in the drop down list should they add another entry. And it would be nice if while they typed, a filtered list of available names matching the partial text were displayed.

Prime candidate for a ComboBox!

My head still hurts from the experience. I would say over 30% of my time, even after BETA started, was spent writing, testing, rewriting, retesting this one dialog. Now a LOT of that can be chalked up with the learning curve for JavaFX2 in general. I am not saying the learning curve is high, but it exists. I learn fast, but not spontaneously!

To be completely fair, the set of requirements around this dialog box were the most complex – the application does a lot of nifty things that the non-tech savvy think is hard – but we know it’s not. Also, the expectations of my BETA testers were high for this screen – it serves a critical role in the process they use, and they had a strong set of expectations about how it should work – after all, can’t I do all that in a web page?

But I digress…

Getting down to it

OK, so a built the box, much like you see above. I made the ComboBox editable, slapped in some property bindings, threw a Key Press Event Handler in the mix for the filter and voila!Mount Vesuvius!

volcano

OK, my fault, too much all at once. Back up, cut out, recompile. Here are the real issues I ran into:
When is an edit not an edit?

When it’s ‘edit on enter only’.  ComboBox shares what I consider to be the same weakness other text controls share in JavaFX2 – losing focus does not commit the edit. Mouse Out? Edit not saved to the bound property. Tab out? No data. TextField does not share this weakness, but TextArea and table cells do. Not sure what the thinking is here, but from a user perspective, they expect when they type something in and tab out that there data is still there. And if they mouse out to the Save button, they certainly expect it to be persisted!

“Yes,” you say, “but if you want that behavior, just code for it.”; to which I say – it shouldn’t be that hard. IMHO it should work that way by default – don’t want it to? YOU code for it. It’s what the users expect…

So I coded for it. Ran into an issue where when the focus handler is called, you can’t tell if the control is gaining focus or losing it (for which I opened JIRA RT-24512), but I went with it anyway. Worked. Sometimes? Most of the time? Users expect all the time. I’ll come back to this.

Stuck in a ditch

OK, so the user starts to type, the event handler kicks in and the drop down is displayed. User keeps typing to enter a new name, hits the tab key because they are done and….nothing. Once the drop down was displayed the tab key no longer would let you exit the control for some reason. Navigation hassle, had to use the mouse. Started a forum discussion, noticed some JIRA issues out there already, so I moved on for now. (I went back today to look for a JIRA issue to reference, but couldn’t find one that I thought captured the issue – EAT)

Oops, did it again

Let me start out with this – I COULD NOT create this problem outside my app, and my app was certainly too complex to include in a JIRA ticket.

I discovered when the dialog was closed and I went back to the parent window – the data showed up! But if I reopened the Attach Subject Dialog, entered a new entry (rather than selecting from the drop down), and closed it again – the new entry showed up, but the old entry was overwritten by the new as well!?

I never found out why, no matter how many experiments I tried. I couldn’t create it outside my app, so I am confident I was doing something wrong, so I re-architected the whole thing. I am sorry to say I stripped out the property bindings at this point. With the unreliability of the edit model and this issue, it was adding too much complexity to the code. I take all the blame on this one, but wondered – should it be this hard?

Out of style

If you read various forum posts, you will discover that styling the elements of a ComboBox is NOT straightforward. Apparently this is true for any of the controls using a Popup Window and the ComboBoxis just a victim. Unfortunately this is not documented in the styling documentation, and excessive forum reading is the only solution.

I just wanted the CB to look like my other controls. It shouldn’t be that hard. I eventually received several good suggestions on the forum and got it to work – well sort of. It worked when I ran the code in Scene Builder, but when I compiled and ran the same code, the styling disappeared. I opened JIRA RT-25674.

Deal Breaker

I was in the last weeks of BETA when I finally had to give up. The final straw was that SOMETIMES when you exited the ComboBox, the edit was lost. This seemed to only occur A) the first time you entered data (never when you selected from list) and B) when the CB was backed by an Object, not a String – that is, when a StringConverter was in the mix. Unfortunately I could not create a little re-creatable program that would allow a JIRA that I created to get any attention. It was the final weeks. I was desperate….

My Waterloo

On December 10, 2012, with a ceremonious post on the JavaFX Forum, I surrendered. People were sympathetic. Some suggested that I never should have used JavaFX2 – it was too new, too immature.

Yeah cause dumping it now, after 8 months of my life and a product almost ready for commercial release, THAT was an option ;). I was committed. Besides, frustration aside I was still a believer!

So I removed the ComboBox and started to code my own.

I didn’t want anywhere near the popup window though. And I was running out of time. This was my LAST bug!

StackPane + ChoiceBox + TextField = ComboBox (if you add a whole bunch of event handlers). It only took me a couple of hours. It was reliable, and I had ultimate control. I was quite proud of myself. Until I accidentally performance tested it!

Turns out there is a bug in the ChoiceBox – JIRA RT-26837 I talk about on the JavaFX2 Forum. It got the attention of Jonathan Giles at Oracle, and he escalated the fix, but it won’t be out until Java8 is released. I didn’t have that kind of time.

StackPane + ComboBox + TextField = ComboBox. OK, its strange I know, using a ComboBox to replace the ComboBox. But it works! The ComboBox does not suffer from the same performance issue the ChoiceBox did. the TextField does not suffer the same weaknesses the edit field of the ComboBox does.

Using the StackPane, I was able to lay the TextField over the edit field of the ComboBox. I made the TextField shorter so that one could still use the drop down arrow of the ComboBox. The ComboBox itself is NOT editable.

       <StackPane fx:id="subjectNameStack">
          <children>
            <ComboBox fx:id="subjectDropdown" focusTraversable="false" maxWidth="1.7976931348623157E308" prefHeight="25.0" prefWidth="150.0" style="-fx-border-color:transparent;" StackPane.alignment="CENTER_LEFT">
              <items>
              </items>
            </ComboBox>
            <TextField fx:id="subjectName" editable="true" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="25.0" prefWidth="130.0" promptText="Person, Company, Place, Thing..." style="-fx-border-color:transparent;">
              <StackPane.margin>
                <Insets right="20.0" />
              </StackPane.margin>
            </TextField>
          </children>
        </StackPane>

When text is entered in the TextField, it stays in the TextField. When a selection is made form the dropdown box, it populates the TextField. There was a lot of extra coding of course:

  • Code to hide dropdown when the TextField loses focus
  • Code to copy dropdown selection to TextField
  • Code for StringConverter for ComboBox (OK I just put that one BACK in)

I was also able to add back the autocomplete filters that I had abandoned in the interest of simplicity weeks before!

    @FXML ComboBox subjectDropdown;
    @FXML TextField subjectName;
 
   @Override
   public void initialize(URL url, ResourceBundle rb) {
       subjectName.setOnKeyReleased(new SubjectFilter());
       subjectName.setOnKeyPressed(new SubjectPressed());
   ...
   }
 
    private class SubjectPressed implements EventHandler {
        @Override
        public void handle(KeyEvent t) {
 
            if(t.getCode() == KeyCode.TAB) {
                subjectDropdown.hide();
            } 
        }
    }    
   private class SubjectFilter implements EventHandler {
        @Override
        public void handle(KeyEvent t) {
 
            if(t.getCode() == KeyCode.DOWN) {
                subjectDropdown.requestFocus();
                subjectDropdown.show();
            } else {
                String partial = subjectName.getText();
                filterSubject(oldPartialSubject, partial);
                oldPartialSubject = partial;
                mySubjectClaim.setId("");
                mySubjectClaim.setRefNo("");
                mySubjectClaim.setName(oldPartialSubject);
                mySubjectClaim.setClaimType("");
            }
        }
    }
    public void filterSubject(String oldVal, String newVal) {
        // If the number of characters in the text box is less than last time
        // it must be because the user pressed delete
        ObservableList tempList;
        if ( oldVal != null &amp;&amp; (newVal.length() &lt; oldVal.length()) ) {             // Restore the lists original set of entries             // and start from the beginning             tempList =  ListManager.getInstance().getSubjectList();         } else {             tempList = subjectDropdown.getItems();         }         // Break out all of the parts of the search text         // by splitting on white space         String[] parts = newVal.toLowerCase(Locale.US).split(" ");         // Filter out the entries that don't contain the entered text         subjectSubentries.clear();         for ( Subject entry:  tempList ) {             boolean match = true;             String entryText = entry.getName();             for ( String part: parts ) {                 // The entry needs to contain all portions of the                 // search string *but* in any order                 if ( ! entryText.toLowerCase(Locale.US).contains(part) ) {                     match = false;                     break;                 }             }             if ( match ) {                 subjectSubentries.add(entry);             }         }         subjectDropdown.setItems(subjectSubentries);         if ( newVal != null &amp;&amp; !newVal.isEmpty() &amp;&amp; subjectSubentries.size() &gt; 0 ) {
            if(!subjectDropdown.isShowing()) {
                subjectDropdown.show();
            }
        } else {
            if(subjectDropdown.isShowing()) {
                subjectDropdown.hide();
            }    
        }
    }
 
    private static class SubjectToStringConverter extends StringConverter {
        @Override
        public String toString(Subject t) {
            return ((t==null)?null:t.toString());
        }
 
        @Override
        public Subject fromString(String string) {
            Subject subject = new Subject();
            subject.setName(string);
            return subject;
        }
    }

Cumbersome perhaps, but it works, it works reliably, and the users don’t know the difference!

Summary

I honestly think if the ComboBox hadn’t been so hard to get working, and if I hadn’t needed it so desperately (what other control could have met user expectations for this type of functionality), I would not have gotten as frustrated as I did. I remember thinking at one time, I don’t CARE about all the SEXY things I am reading on the blogs and through Twitter. I just want basic controls that were easy to use and reliable!

Now that’s not fair; I know the people experimenting with the sexy side of JavaFX aren’t the ones responsible for UI functionality – they are developers doing wonderful things with a new technology, and they will ultimately be responsible for the widespread acceptance of JavaFX2. I get that. I WANT that.

But my potential customers don’t care that I used JavaFX to write Evidentia. I can’t say “It’s a limitation of the technology”. They want apps that work, work as expected, and work reliably.

(I did have to claim that in one instance for functionality that just isn’t there for MACOS, but apparently MAC users are used to that, and were happy to get the app any way. Poor excuse, I know, and I can’t wait until RT-153 and RT-20784 are addressed!)

I just hope Oracle is paying attention to the less sexy side of things. Users expect that anything they can do on a web page they can do in a desktop app. Developers expect that anything they can do in Swing they can do in JavaFX.

Me? I just want to write nifty genealogy applications, not JIRA tickets.

The world moves on. JavaFX will get better. Jave 8 will solve everything .

Two out of three ain’t bad.

Posted in Evidentia, JavaFX | 5 Comments

Custom Page Templates in RootsPersona 3.1

There is an excellent post about the new Custom Page Templates feature that was added to Rootspersona in 3.1

Check it out at Web503!

And a great tutorial on using shortcodes and adding tabs to your Perosna page!

http://web503.com/tips-n-tricks/wordpress-user-tutorials/how-to-make-the-perfect-rootspersona-custom-shortcode/

Ed

Posted in Uncategorized | Leave a comment

RootsPersona 3.1 is live!

It has been a while since I have put out an update to RootsPersona, but a compatibility issue with WordPress 3.5 (actually with the latest jQuery version) and a generous donation from a sponsor resulted in a pre holiday special.

3.1 is compatible with 3.5. There are also a couple of other bug fixes (404 issues with permalinks and a faulty rootsPersonaBio shortcode). The bug new feature is the ability to define a page ‘template’ on the options screen that replaces the [rootsPersona] shortcode AT RUNTIME.

It’s an advanced feature, and not for everyone, but it is kind of slick. The sponsor, Charles, has promised to post about his experience with the new feature, and I will post a link to his blog when he does.

Enjoy!

Posted in Uncategorized | Leave a comment

JavaFX from the Trenches – Part 1 – Native Packaging

I recently released a commercial app (Evidentia) for the PC and MAC. That’s right, PC and MAC AT THE SAME TIME!

In my analysis of languages I code in, Java, and by extension JavaFX2, comes the closest to a true “write once, deploy anywhere” framework. However the process is NOT painless, and in this series of “from the Trenches” posts, I plan to document lessons learned in hopes that others may suffer less.

Let me start by saying that I am a fan of JavaFX2. Although Oracle will not come out and say that JavaFX2 is a replacement for Swing, well come on, someone should. OK I will – as far as I am concerned JavaFX2 is a replacement for Swing. When I looked ate frameworks, it eventually came down to Swing versus JavaFX2, and JavaFX2 won hands down.However, in this series I plan to be open, honest and direct including stating matter-of-factly st the start that JavaFX2, or more accurately 2.2, is not as stable as one would expect a 2.2 version to be.

More on that in future posts. On the the good parts! let’s start at the END.

Native Packaging

My original plan had been to generate Java jar files and deploy a zip file as my production ready distribution – after all, everyone has Java loaded on their machine, right? Dumb idea. My target users are not uber geeks, and I couldn’t make those assumptions. Once I decided I was selling this product instead of open sourcing it, I had to up my game.

Enter Packaging for JavaFX - more specifically, Self Contained Application Packaging. With a few ant tasks added to my build file (and a few third party downloads), my app would be packaged 1) with the correct version of the Java libraries, and 2) in a traditional installation file format that would be recognized by most users, whether on the PC or the MAC.

Being MAC illiterate, you can imagine what appeal the Self Contained Application Packaging option held for me.

The caveat? You have to build the Windows installer on a Windows machine, and the MAC installer on a MAC with a 64bit OS. This makes sense if you think about it, but I didn’t have a MAC. So a week before BETA testing was to start, I bought a used iMAC off Craigslist and started cramming.

Posted in Evidentia, JavaFX | 3 Comments

RootsPersona 3.0 is live!

That’s right, RootsPersona 3.0 is live!

After two months of development and beta testing, the 3.0 release of RootsPersona is ready for prime time! Thanks to Mark Harris, who sponsored the enhancement, website managers may now edit their genealogy data within the plugin.

Simply check the System of Record YES box on the options page, and new edit features will be unlocked.

The new version also supports enhanced error reporting, support for multi-site WordPress installs, and Top Ten Surname widget for your sidebar.

Posted in News, rootspersona | 4 Comments

Generous User Sponsors Enhancement.

Due to popular demand and the support from a generous sponsor, I am currently adding the ability to edit persona data from within the program. This will allow someone with edit privileges to add/edit persons, including almost all the fields displayed by rootspersona.

Users may add a person and build a tree from scratch, or import a GEDCOM and add/edit persons from the GEDCOM as a starting point (though it is NOT recommended to upload another GEDCOM once you start editing directly in rootspersona).

Release 3.0 is targeted for release in May 2012.

If YOU have a favorite enhancement you would like to sponsor, feel free to email me!

Ed

Posted in News, rootspersona | Leave a comment

Microdata added to rootspersona

I thoroughly enjoyed watching the live feeds from RootsTech 2012, tweeting at the same time.

Up until then I had not payed much attention to HTML5 attributes. Not that I wasn’t looking forward to a world without Flash, I just didn’t have time. But after RootsTech, I found an incentive.

Microdata is a new feature within HTML5 that allows data ABOUT the data to be embedded in the HTML. Search engines can then use the ‘microdata’ to maximize matches.

So as of version 2.4.0 (deployed this evening) rootspersona includes microdata!

The schema is defined at historical-data.org. Microdata has been added to the following panels:

  • Header
  • Ancestor
  • Descendancy
  • Group Sheets

Search engines can now tell that a name is a name, a birth date is not only a date, but a date of birth, and that ‘Attleboro, MA’ is a place of birth; or death.

Posted in Uncategorized | Leave a comment

rootspersona DEBUG

OK, after working with many folks on how to debug rootspersona, I have added a debug flag to the options menu in version 2.3.1. If you are having a problem, please:

1) check the debug box on the rootspersona options screen
2) execute the problematic code
3) send me the output from http://yoursite/wp-content/debug.log
4) you can uncheck the debug box if you wish

This will help immensely in me helping you.

Posted in Uncategorized | 1 Comment