My ongoing struggle with stupidity and what is not valid JSON

So this was a frustrating lesson to learn – and please feel free to be entertained by my apparent stupidity.

I was trying to create my own JSON with an xAgent – seems easy enough. I created a simple output which looked like this

{
    'items': [
        {
            'name': 'AK',
            'dc': 23
        }
    ]
}

and when I fed it through my AJAX code it failed.

$.ajax({
	dataType: 'json',
	url: "http://copper.xomino.com/xomino/ExtjsBigData.nsf/xState.xsp?query="+query
	//url: 'http://copper.xomino.com/xomino/Extjs.nsf/xRestService.xsp/byFirstName?count=5'
}).done(function(data){
	console.log(data)
}).fail(function(e){
	console.log('FAIL')
	console.log(e)
})

But what was interesting is that it returned the data (e) correctly. I was baffled and frustrated for way too long.

So I finally asked he correct question of google (isn’t that always the case) and I found that http://jsonlint.com/ was telling me the strings were incorrect…..

because JSON requires double ” and not single ‘

really?

This worked – changed the single to double quotes.

{
    "items": [
        {
            "name": "AK",
            "dc": 23
        }
    ]
}

and I am apparently now less stupid that I was yesterday…….

PS

Kathy recommends – http://json.parser.online.fr/

Prototypal inheritance of SSJS across the whole server in XPages

A friend asked me the other day how would I determine where a string lay within a JavaScript Array. I of course answered – “it depends! If you are using newer IE or a non-crappy implementation of the JavaScript engine then use Array.indexOf – it has been around for years…..”

var arr = ["simon", "marky", "david"]
return arr.indexOf("marky") // 1.0

Well – turns out the friend was asking about SSJS in XPages and it turns out that apparently XPages has one of those crappy JavaScript engines of which I spoke and Array.indexOf is not supported (facepalm). (*really??*)

<xp:text escape="true" id="computedField1">
	<xp:this.value><![CDATA[#{javascript:
		var arr = ["simon", "marky", "david"]
		return arr.indexOf("marky")
	}]]></xp:this.value>
</xp:text>

arr1

Polyfill

OK so there is a simple polyfill for the indexOf, which has also been around for years as this article written in 2007 demonstrates…..

if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]==obj){
                return i;
            }
        }
        return -1;
    }
}

And if we add this to our XPages sample code we get the answer we are expecting….

<xp:text escape="true" id="computedField1">
	<xp:this.value><![CDATA[#{javascript:
		if(!Array.indexOf){
		    Array.prototype.indexOf = function(obj){
		        for(var i=0; i<this.length; i++){
		            if(this[i]==obj){
		                return i;
		            }
		        }
		        return -1;
		    }
		}

		var arr = ["simon", "marky", "david"]
		return arr.indexOf("marky")
	}]]></xp:this.value>
</xp:text>

arr2

Yay !

Prototyping in JavaScript

For a better explanation that I can give, check out this StackOverflow answer on what is prototyping – suffice to say that every JavaScript object has a prototype property and this is the basis for prototypal (as opposed to class based) inheritance in the JavaScript language.

What we did in the polyfill above is: we simply said if Array.indexOf method does not exist, then we created one.

So what does this have to do with XPages?

In JavaScript when you prototype a function, the scope is on the page you are looking at. As soon as you leave the page the prototype is gone. In XPages it seems to be a little broader than that…..

I (unintentionally) discovered that once you prototype the Array.indexOf, not only does it become available on the same page, it is permanently on the same page. You run the prototype code once….then take it away, and it still works. The scope would appear to be at the application level…..

Well so then I asked some people who are way smarter than I and Nathan Freeman pointed me to his article about XPages Performance Pro Tips and told me he expected that the prototype would actually be at the server level and he was right….

I included the URL of the test database in the picture above to demonstrate that when I move the simple code to another database – it still works (after the prototype code has been run once)

rr3

And just to be sure – in another browser !!

arr4

Once the Array.prototype.indexOf was created, it became available in all applications on the server, instantly.

Killing the prototype

  • Restarting HTTP *DOES NOT* stop this.
  • Killing all browsers and starting again does not stop this

As far as I can tell you have modified the way the SSJS works on the server until you kill it. So far I have run a test over 24 hours later and it is still working……

This is COOL right?

Well for one it means that you can seriously enhance all your JavaScript on the server in ways which make custom libraries look like a waste of time. We could truly add all the corporate libraries to SSJS once when the server loads and they would always be available to all developers at all times.

arr5

And the even more awesome (depending on perspective…..)

arr7

Well yeah but…….

You can also serious screw up perfectly good code across the server

arr6

Conclusion

Prototypal inheritance is at the root of JavaScript programming language and is very powerful. What we can see here though is that we are able to affect code across the entire server with a few lines of code. This is *very* cool and *very scary* at the same time.

So with this knowledge comes great responsibility – use it at your own risk and make sure you have very good server side code review if you are going to go down this path.

Sorting an unsorted XPages document collection based on a date field

In one of my recent applications I had a requirement to return the top 500 search results, but sort them by a specific date field on the document, in descending order. Due to the size of the database doing a view.FTSearchSorted was not feasible as it was too slow. So I searched around and found some code for sorting a JavaScript Array of objects.

Creating the array

Search the database and push the search results into a JavaScript array

var sSearch = "[FIELD Subject contains smith]"
var dc:NotesDocumentCollection = db.FTSearch(sSearch, 500)
var doc:NotesDocument = dc.getFirstDocument();
var myArray=[]
var i=0
var sTemp = ""
while (doc != null) {
	i=i+1
	var obj={}
	obj.count=i
	obj.Customer = doc.getItemValue('Customer')
	obj.malfunctionEnd = @Text(doc.getItemValue('dateField'))
	obj.unid = doc.getUniversalID()
	myArray.push(obj)
	var tmpdoc = dc.getNextDocument();
	doc.recycle();
	doc = tmpdoc;
}

The sort function

Create a function to return a bubble sort of dates. This function works by comparing if two dates should be sorted based on their “getTime()” which returns a value in seconds.

function custom_sort(a, b) {
var ndtA:NotesDateTime = session.createDateTime(a.dateField);
var ndtB:NotesDateTime = session.createDateTime(b.dateField);

return ndtB.toJavaDate().getTime() - ndtA.toJavaDate().getTime()
}

The final step

Using the JavaScript sort function we can pass in the name of a callback function which adds additional processing to the sort function. The resulting array can then be processed by a repeat control providing the information out to the end user. All the code shown here would be placed in the value parameter of the repeat control.

myArray.sort(custom_sort);
return myArray

Mustache.js – How to create a client side Repeat control in XPages

Whether or not you realize it, everyone who uses a repeat control in XPages development is actually using a server-side template. Let me demonstrate the concept….

We create an object through many different interactions with the XPages back end APIs and Server-Side JavaScript interactions. The object can be many different constructs. The XPages repeat control then iterates through each member within the object and repeats the same thing over and over based on the repeat control’s starting value object.

In the following example we create a repeat control from a JavaScript array control. It iterates over each object within the array allowing the repeat control to work as a template for creating HTML.

<xp:repeat id="repeat1" rows="30" var="myObj">
	<xp:this.value><![CDATA[#{javascript:
	var myArray=[
		 {
			  "@entryid":"1-431C28F894B0548185257C22000A8403",
			  "firstname":"Agnes C.",
			  "lastname":"Smith",
			  "address":"24256 Leisure Lane"
		  },
		  {
			  "@entryid":"2-4ED02366A420D23985257C4F000B0BD7",
			  "firstname":"Albert",
			  "lastname":"Scoggins",
			  "address":"3525 Koontz Lane, second"
		  },
		  {
			  "@entryid":"3-6D8120FA75B4BCCF85257C4F000B0B5D",
			  "firstname":"Alejandra",
			  "lastname":"Dodge",
			  "address":"3044 Simpson Avenue"
		  }
	]

	return myArray}]]>
	</xp:this.value>
	<p>	Hello my is
		<strong>
		<xp:text escape="true"
			id="f1" tagName="h4" disableTheme="true"
			value="#{javascript:myObj.firstname +' '+ myObj.lastname}">
		</xp:text>
		</strong>
	</p>
	<br />
	<p>
		I live at
		<xp:text escape="true"
			id="f2" tagName="h4" disableTheme="true"
			value="#{javascript:myObj.address}">
		</xp:text>
	</p>
	<hr />
</xp:repeat>

The output from this repeat control is shown below

mp1

HTML Templating

Using the mustache.js library we are able to display a JavaScript object using an HTML repeat control in a directly analogous way to the XPages server-side method described above. The following code does pretty much the same as the server side example above.

What is interesting to note is the use of the SCRIPT tag in the following code. By giving it a type of “text/template” the browser does not execute the contents of the SCRIPT tag. But it also ignores the HTML within. We can access the contents of the script as it is a DOM object. This makes for the perfect storage container – we do not have to hide the template HTML with CSS.

The HTML

<!-- mustache uses AMD and as such will not work in R9 unles you add it this way *stupid dojo 1.8 issue*-->
<xp:this.resources>
		<xp:headTag tagName="script">
			<xp:this.attributes>
				<xp:parameter name="type" value="text/javascript" />
				<xp:parameter name="src" value="js/mustache.js" />
			</xp:this.attributes>
		</xp:headTag>
</xp:this.resources>

<script type="text/template" class="template">
{{#items}}
	<br /><p>	Hello my is
		<strong>
		{{firstname}} {{lastname}}
		</strong>
	</p>
	<p>
		I live at
		{{address}}
	</p>
	<hr />
{{/items}}
</script>
<div class="templateHere"></div>

The JavaScript

$('document').ready(function(){

	var myArray={
		items:[
		 {
			  "@entryid":"1-431C28F894B0548185257C22000A8403",
			  "firstname":"Agnes C.",
			  "lastname":"Smith",
			  "address":"24256 Leisure Lane"
		  },
		  {
			  "@entryid":"2-4ED02366A420D23985257C4F000B0BD7",
			  "firstname":"Albert",
			  "lastname":"Scoggins",
			  "address":"3525 Koontz Lane, second"
		  },
		  {
			  "@entryid":"3-6D8120FA75B4BCCF85257C4F000B0B5D",
			  "firstname":"Alejandra",
			  "lastname":"Dodge",
			  "address":"3044 Simpson Avenue"
		  }]
	}
	var e, newStuff, template;
	try {
		 template = $('.template').html();		//Get the HTML from within the SCRIPT tag with the template class
		 newStuff = Mustache.to_html(template, myArray)	//Apply the myArray object to the template HTML
	   } catch (_error) {
		 e = _error;
		 newStuff = e.toString();
	   }
	$('.templateHere').html(newStuff)
})

Here is the web page created
mp2

Below you can see a direct comparison between the repeat control code and the HTML template code

mp3

Implementing a REST service

The example above uses a typed JavaScript object to create the data for the template – and you have probably realized the example I used is the same format as a XPages REST service <xe:viewItemFileService> feed. So with a little bit of AJAX to put in the data object we are able to create the same output using REST data rather than typed object.

$('document').ready(function(){
	var e, newStuff, json, template;
	$.ajax({
		url: 'xRestService.xsp/byFirstName'
	}).done(function(data){
	try {
	     template = $('.template').html();
	     newStuff = Mustache.to_html(template, data)
	   } catch (_error) {
	     e = _error;
	     newStuff = e.toString();
	   }
	   $('.templateHere').html(newStuff)
	})
})

Except in this case we loaded *all* the names in the REST service into the page using the template – in 15 lines of code

mp4

Conclusion

While there may not, on the surface, seem to be much point in using HTML templating when we have server-side repeat control templating, if you want to take your web applications running on XPages into the future, this is the beginning…………More to come on this topic 🙂

Troubleshooting JavaScript library errors in XPages

Coincidentally on three separate occasion, with three different customers/developers using three different libraries I encountered the same issue last week. I am going illustrate the process I go through when troubleshooting JavaScript libraries and use this one issue as the example.

The same question from each of them: Why is my JavaScript library not working in my XPage?

What does Firebug say?

If you ever ask me for help this will always be my first question – and a significant number of issues can be solved by just looking at the browser console and taking action based on knowing what the failure is.

Typical errors

Here are some typical errors/messages and what can be done about them

  • $ is not defined
    • Problem: This means you have tried to use jQuery (most likely) before the jQuery library is loaded. jQuery plugins are dependent on the jQuery library being loaded before them in the browser.
    • Solution: check that you are adding jQuery, the path is correct and it is being loaded before any other library dependent on it.
  • 404 file not found
    • Problem: Looking through the Net tab in firebug you will be able to check and see if all your files loaded correctly
    • Solution: Right click on the failing URL and open in new tab then you can see the whole URL.
      • Check to make sure the file is in fact where you think it should be
      • Check your spelling
      • Check to make sure that your new design element is built. XPages will report 404 file does not exist if you have not built the new element. Even though it is in your database does not mean it is available.
  • 400 Bad request
    • Check and make sure that the library has not appended anything to the end of the file name making it illegible to the Domino server (see Caching below)
  • General JavaScript errors
    • Problem: a lot harder to troubleshoot. You will get a message saying that the dojo.js or jQuery.min.js has reported an error and it looks like the library has a bug. Chances are though that it is a coding error or a configuration error on your part which is bubbling up as an error in the library using the code you have created.
    • Solution: work back to the example and see where your code is different.

Caching

Many JavaScript libraries append random values to the end of files with the positive intention of avoiding browser cache issues. Ext JS is one example of this – when you ask for the required files within the grid “random crap” gets added to the end of the file name:

Ext.require([
      'Ext.data.*',
      'Ext.grid.*',
      'Ext.ux.grid.FiltersFeature'
    ]);

When the page loads you see nothing and this issue in Firebug

err1

So when we right click and open the file in a new tab we see the following URL in full

The answer is really very simple – IBM Domino is a web server waiting for requests, and if you (or the library you are trying to use) asks the server for something it does not understand it will reject you with a 400 bad request error. In this case it is trying to interpret the _dc= command on the FiltersFeature.js file and failing.

There are two possible solutions and depending on the library configuration you should be able to work around it

  1. Turn off caching if you believe it will not cause issues
  2. Modify the URL of the incoming file by adding ?open& on it

In the case of Ext JS we add the following code to turn off caching

Ext.Loader.setConfig({
  enabled : true,
  disableCaching : false //This turns off caching - nice double negative I know (v4 and above)
});

Ext.require([
      'Ext.data.*',
      'Ext.grid.*',
      'Ext.ux.grid.FiltersFeature'
    ]);

But it *must* come in your code *before* the require code otherwise it won’t work.

Require.js usage in XPages R9

As has been detailed by Sven Hasslebach, along with his solution, Dojo 1.8 (used in R9) does not play at all with other AMD libraries. If your JavaScript library used require.js or an equivalent loader library you must make sure it is loaded in the header before dojo. Check out Sven’s post for the solution/workaround.

Conclusion

Troubleshooting is always easier when you have a checklist of possible issue to go through. The more you do them the more they will become muscle memory. If anyone has any other good tips please feel free to add in the comments 🙂

Ext JS Connect 14

This post is in support of the presentation I gave at IBM Connect 2014 – 28th January 2014

To find the demonstrations:

http://demo.xomino.com/xomino/extjs.nsf

To find the Ext JS 4.2 examples

http://docs.sencha.com/extjs/4.2.0/extjs-build/examples/

To find the slide presentation from Connect

http://www.slideshare.net/MarkRoden/presentations

I hope everyone that attended enjoyed the presentation – please feel free to ask questions and / or hit me up on twitter @MarkyRoden

Polyfill for IE8 and the XPages REST service date format

In my previous post I talked about the date format coming from an XPages REST service – the ISO date format. Well it turns out that IE8 and below do not recognize this date format and will be very upset at you if you try and create a new Date() from it. (It actually return NaN because Dates are really numbers 🙂 )

Example REST data

{
  "@entryid":"21-6CE8F65D7CB43A5185257C06000B8F00",
  "StartDate":"2013-07-01T01:19:19Z",
  "Form":"Position",
  "Title":"Worker"
}

new Date("2013-07-01T01:19:19Z") // NaN in IE8

So I searched the Google-webs and found a polyfill solution which will re-parse the ISO date format back into something all browsers will recognize. This is a necessary evil in my current application but if you do not have to support IE8 then don’t waste to computation.

Add the following code to your JS libraries and use the Date.fromISO() to crate your dates as stated in the original StackOverflow

/*
  http://stackoverflow.com/questions/11020658/javascript-json-date-parse-in-ie7-ie8-returns-nan
  In older browsers, you can write a function that will parse the string for you.
  This one creates a Date.fromISO method- if the browser can natively get the correct date from an ISO string, the native method is used.
  Some browsers got it partly right, but returned the wrong timezone, so just checking for NaN may not do.
  Polyfill:
*/
(function(){
    var D= new Date('2011-06-02T09:34:29+02:00');
    if(!D || +D!== 1307000069000){
        Date.fromISO= function(s){
            var day, tz,
            rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
            p= rx.exec(s) || [];
            if(p[1]){
                day= p[1].split(/\D/);
                for(var i= 0, L= day.length; i<L; i++){
                    day[i]= parseInt(day[i], 10) || 0;
                };
                day[1]-= 1;
                day= new Date(Date.UTC.apply(Date, day));
                if(!day.getDate()) return NaN;
                if(p[5]){
                    tz= (parseInt(p[5], 10)*60);
                    if(p[6]) tz+= parseInt(p[6], 10);
                    if(p[4]== '+') tz*= -1;
                    if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
                }
                return day;
            }
            return NaN;
        }
    }
    else{
        Date.fromISO= function(s){
            return new Date(s);
        }
    }
})()
/* end Polyfill */

  http://stackoverflow.com/questions/11020658/javascript-json-date-parse-in-ie7-ie8-returns-nan

and if you need to use it – vote it up on SO !!

You could almost certainly correct this on the server by creating a custom column in your REST service to re-format the back end document date field and send out whatever format you wanted. If you are working with a JavaScript framework or front-end library I recommend against this because you really want your JSON to contain the correct date object. Especially in a localized application you are setting yourself up for unnecessary risk.

Dealing with Dates, and localization in XPages

In this article I will discuss and demonstrate how to deal with dates in a localized application. We will look at the dojo.locale() library and  discuss how localization affects dates in XPages.

Background

Dates in JavaScript – nasty horrible things, to be avoided at all costs. So much so that there are a number of date handling specific libraries including moment.js and date.js. In addition to these two Dojo has it’s own built in date parsing library and that can also be utilized with multiple languages (localization) and the jQueryUI datepicker functionality also allows for date formatting. So we have a number of options depending on how we want to utilize the available resources.

Localization

Localization is the fancy term for making your application multilingual. For a more in depth look at how to do that at the application level check out http://www-10.lotus.com/ldd/ddwiki.nsf/dx/XPageLocaleTimezone.htm or go see Brad and Kathy at this year’s connect14.

When you turn localization on within your XPages application (R9 – Application properties / Design /International Options) the server generates a dojoConfig.locale variable at the top of your XPages webpage.

date1

This gives us programmatic access to the language at anytime.

For those interested the actual language is passed to the server through the header information when requesting the page. In this case I am telling the server that I was one of three languages (should the application support them)

  1. en, en-us (american english)
  2. fr (french)
  3. de (german)

date2

Within the XPages international options I can specify the languages – and the server will send out the first language supported by the application which coincides with what the browser is asking for.

So if the application supports french, dutch and german – I will get a french page served to me because that comes before german in my list of languages.

Most Americans who have never had to deal with this are thinking – who cares everyone uses mm/dd/yyyy right??? And they would be wrong. Almost nobody else deals with mm/dd/yyyy and if you manually validate mm/dd/yyyy for an international application you will fail in other countries.

Dojo

In XPages we get dojo out of the box and with that we get date formatting functions. The dojo/date/locale() “Creates a string from a Date object using a known localized pattern.“. It is really rather nice and simple and as you can see from the documentation page there are *many* different formats in which you can return the date.

I needed to display the day of the week, month, day number and year at the top of a page, so I created my own date translation function in the code as follows. What this function does is that it accepts a date object and a pattern. The function then returns a string based on the pattern.

require(["dojo/date/locale"])

function formatLocaleDate(date, pattern){
	//example formatLocaleDate(date, "EEE MMM d yyyy")
	return dojo.date.locale.format(
		    date,
		    {
		    	selector: 'date',
		    	formatLength: "short",
		    	locale: dojoConfig.locale,
		    	datePattern: pattern
		    }
		)
};

So in this case the example given would return

“lun jan 13 2014” in french
“mon jan 13 2014” in english

But this still does not determine if mm/dd/yyyy is a valid format in your date field.

Here is the simple date test for that.

If you try the following and dojoConfig.locale = ‘en-us’ you will not pass. But in ‘fr’ you will get a date object.

var temp = dojo.date.locale.parse('20/12/2013',
    {
    locale: dojoConfig.locale,
    formatLength: "short",
    selector: "date"
  })

if(temp){
    console.log('pass')
}

The REST service
A rest service date field comes along in ISO format

[
  {
      "@entryid":"1-AE3FB4C32057B87C85257C22000A83DD",
      "firstname":"Alfred",
      "lastname":"Hall",
      "address":"2139 Jail Drive",
      "state":"IL",
      "city":"Tremont",
      "noteid":"NT0000BD22",
      "lastMod":"2013-12-20T06:00Z" //ISO date format
  },

and because of that it is language independent (smart that). So if we want to turn that into something appropriate for the french (dd/mm/yyyy) or american (mm/dd/yyyy) we have to use the dojo locale library.

var temp = new Date('2013-12-20T06:00Z')
temp = dojo.date.locale.format(temp,{
    selector: "date",
    formatLength: "short"
  });

In American English we get : “12/20/2013”
In french we get : “20/12/2013”

In this way we get the correct formatting without having to write code in each language.

Conclusion

How ever you are dealing with date formatting, date validation or REST service JSON data (in my case in Ext JS) there are dojo date formatting functions available for free on the server. If you need localization then even better because you life is may significantly simpler than it would otherwise be.

PS 

Before anyone starts to have a go about ooo Marky used Dojo….?!?! Yeah I did, because it made sense to meet the requirement. Would I use Dojo to meet this requirement in a non-XPages application? Probably not 🙂

One way to validate that two time ranges don’t overlap in JavaScript

Today’s programming challenge was to validate that two time ranges in the same day were not overlapping.

I had written over 50 lines of JavaScript with if this is before that then if this if after that other one then this – OR – the same in reverse and so on and so on. It was a lot of what seemed unnecessary code – un-elegant was how it felt (if that is a word).

So in the spirit of approaching a problem differently (no bullshit box terms here) I came up with the following solution.

We have time range A: 1AM to 3AM
We have time range B: 3AM to 4AM

or we could have the second range before the first

Range A: 3pm to 5pm
Range B: 1pm to 3pm

and I need to make sure they do not overlap – that is the only requirement

So I figured: if I make an array of every minute representing each time range and compared them, if there is no matches then there is no “overlap”.

So for the first time range I create an array of the minute-in-the-day (e.g. 1:01 am would be 61) from start to finish which looks like this [60, 61, 62, 63, 64] and then create another array of the second range [120, 121, 122, 123 ,124 etc] and do a comparison. If no matches then no overlap.

here is the functions:

function createMinuteArray(date1, date2){
	//this function creates an array of the number of minutes (in the day) between the start and end time
	// e.g.[60, 61, 62, 63]

    var arr = []

    var startMinute=(date1.getHours()*60)+date1.getMinutes()
    var endMinute=(date2.getHours()*60)+date2.getMinutes()

    for (var i=startMinute; i<endMinute; i++){
        arr.push(i)
    }

    return arr
}

function diffArray(arr1, arr2){
	//this function runs a test on all the values in arr1 and checks to see if that value is in arr2
	//if that is the case then there is overlap of the arrays and therefore return true
    for (i=0; i<arr1.length; i++){
        //I used jQuery inArray because I have IE8 requirement and arr1.indexOf is not supported
        if ($.inArray(arr1[i], arr2)>-1){
            //we have a match and therefore an overlap
            return true
        }
    }
    return false

}

and this is how they are called

var mealbreak = createMinuteArray(startTime1, endTime1)
var otherbreak = createMinuteArray(startTime2, endTime2)

//Check to see if any of the minutes in one array overlap with the minutes in the other one
var overlap = diffArray(mealbreak, otherbreak)

From there we can determine if(overlap) then fail validation

This could be used for comparing dates but would really be impractical – because that would be enormous arrays to create and compare.

I posted this as much in the spirit of – hey this is a different way of solving a problem, more so that a recommendation for how you should validate dates and times. Just something different 🙂

Remove all classes using dojo and jQuery

One of last week’s challenges on the side was to take this Connections wiki page and make it readable on a small screen – if you drag the right hand side over to the left you will see that the right navigation menu overlays the non-wrapping text. The main section is forced to a 980px width !

http://www-10.lotus.com/ldd/lcwiki.nsf/dx/Best_Practices__Troubleshooting_Lotus_Connections_3.0#Traces

A very brute force approach to this is to remove all the classes from all the elements on the page

In jQuery you would do this:

$('*').removeClass()

in dojo (which the page is written in) it takes slightly longer

dojo.forEach(dojo.query('*'), function(item, i){
    item.className=""
})

It would be relatively simple to turn this into a bookmarklet to run for yourself

Here is a bookmarklet you can add to do it for you – this will also work on other IBM wiki pages

Create any link on your bookmark bar and then edit it. Copy and paste the following as your URL

javascript: dojo.forEach(dojo.query(‘*’), function(item, i){ item.className=”; })

b1

When you go to the wiki page click the bookmarklet and all classes will be removed – this is only work on pages which have dojo on them already (which IBM documentation does).