CKEditor 4 Required Field Validation

I’ve used FCK Editor for years, but now with the FireFox 17+ incompatibility it’s time to upgrade to CKEditor.

I ran into some “stupid challenges” along the way. For example I tired out my brain trying to get it to work with jQuery. It appears in CKEditor v3.x there was a jQuery adapter. But in version 4 it wasn’t included. I tried to reuse it, but that seems to fail horribly for me. So I finally gave up on that concept. If anyone has a good way to implement jQuery with CKEditor I’d love to hear from you!

To get things rolling a just call the ckeditor.js file and add the class “ckeditor” to my textarea.

<form>
    <textarea class="ckeditor" id="noticeMessage" name="message"></textarea>
</form>
<script type="text/javascript" src="ckeditor/ckeditor.js"></script>

Now I want to verify that there’s actual text entered. To do that I remove all HTML markup and return the length of the string returned from the CKEditor API. The “noticeMessage” for the “instances” variable is the ID of the “textarea” element.

<form>
    <textarea class="ckeditor" id="noticeMessage" name="message"></textarea>
</form>
<script type="text/javascript" src="ckeditor/ckeditor.js"></script>
<script type="text/javascript">
    $("form").submit( function() {
        var messageLength = CKEDITOR.instances['noticeMessage'].getData().replace(/<[^>]*>/gi, '').length;
        if( !messageLength ) {
            alert( 'Please enter a message' );
        }
    }
</script>

Now if we test this with the word “test” it returns 5 characters. Well that’s not good, because last I knew math “test” only contains 4 characters. If we remove “test”, the correct value of 0 is returned. In this case it really doesn’t matter much because I’m just looking to see if anything is there. But it was annoying me and could prove bad in the future for further tests if I forgot about the issue.

The reason for this is if you view the source it inserts a line return automatically during editing. Normally I can fix this quite easily with jQuery, but because that concept made my head explode, I had to find a different way.

The trim() method didn’t come along in JavaScript until version 1.8.1. So while my current browser users are okay, my older browsers users such as IE8 aren’t so lucky.

To resolve this issue, I test for the “trim()” prototype method. If it doesn’t exist I extend the String prototype object with a trim() method. So thanks to some guy with the alias “Timo” I’ve put this thinking into place. The length result of string “test” now returns an accurate value of 4.

<form>
    <textarea class="ckeditor" id="noticeMessage" name="message"></textarea>
</form>
<script type="text/javascript" src="ckeditor/ckeditor.js"></script>
<script type="text/javascript">
    /**
     * Compatibility fix for trim()
     * Browsers 3.5+, Safari 5+, IE9+, Chome 5+ and Opera 10.5+ support trim() natively
     * So we're detecting it before we override it
     * This is here because CK Editor isn't playing nice with jQuery
     * Thanks to http://blog.stevenlevithan.com/archives/faster-trim-javascript
     */
    if(!String.prototype.trim) {  
        String.prototype.trim = function () {  
            var c;
                 for (var i = 0; i < this.length; i++) {
                     c = this.charCodeAt(i);
                     if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12) continue; else break;
                 }
                 for (var j = this.length - 1; j >= i; j--) {
                     c = this.charCodeAt(j);
                     if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12) continue; else break;
                 }
                 return this.substring(i, j + 1);
        };  
    }

    $("form").submit( function() {
        var messageLength = CKEDITOR.instances['noticeMessage'].getData().replace(/<[^>]*>/gi, '').trim().length;
        if( !messageLength ) {
            alert( 'Please enter a message' );
        }
    }
</script>
Advertisements

#ckeditor, #javascript, #validation

Handling Expired Sessions via AJAX & FW/1

This is a followup to my “Framework One AJAX Method (FW/1)” post (https://christierney.com/2012/07/14/framework-one-ajax-method-fw1/).

Scenario:

  1. You use the session scope to define if a user is logged in or not
  2. You use jQuery AJAX to pull JSON data from FW/1 action URL’s
  3. The user’s session has expired after x minutes of inactivity after login
  4. If the session is expired the user is directed to a login page after trying to navigate

So what happens in this scenario? Instead of the expected JSON data your AJAX call receives the HTML of a login page with a status of 200. Can’t do too much with this.

Here’s a code example that will pass the client a 403 error (Forbidden) in the header and return no content. jQuery will then redirect the user to a login screen when it sees this status code.

First here’s a simlified FW/1 Application.cfc setupRequest() method:

void function setupRequest() {

	var reqData = getHTTPRequestData();

	if( structKeyExists( reqData.headers, 'X-Requested-With' ) && reqData.headers[ 'X-Requested-With' ] == 'XMLHttpRequest' && !structKeyExists( session, 'user' ) ) {
		getpagecontext().getresponse().setstatus( 403 );
		abort;
	}
}

This code detects if the call came from an AJAX request ( getHTTPRequestData().headers.X-Requested-With = ‘XMLHttpRequest’ ) and if the session still knows about the user. If it is an AJAX request and the user is not known, then set the status code of the return page to 403 and stop processing any more code. If you try to use throw instead of abort, it will overwrite the status code to 500.

The second simple example is the jQuery piece:

$( document ).ready( function() {

	$( this ).ajaxError( function( e, jqXHR, settings, exception ) {
		if( jqXHR.status == 403 ) {
			location.href = '?logout';
			throw new Error( 'Login Required' );
		} else if( !jqXHR.statusText == 'abort' && jqXHR.getAllResponseHeaders() ) {
			alert( 'There was an error processing your request.\nPlease try again or contact customer service.\nError: ' + jqXHR.statusText );
		}
	});

});

Here we are globally looking at all AJAX requests. Since the status code 403 is in the error class it will throw an error. The .ajaxError() method picks up this error and handles it.

If the status code is detected as a 403 (which we set in our ColdFusion code) then we direct the user to a logout page (which in turn directs to a login page) and throws a JS error. The throw statement is supposed to stop all JS processing, however if you have an error handler attached to the specific AJAX call, then that will still fire. The error message will just be seen if you are viewing the JS console.

If there’s another error caught it first looks to see if the request was aborted or if the user navigated away from the page. In these two cases I don’t want to display an error. If anything else is caught, I display a generic message.

#ajax, #coldfusion-2, #fw1, #jquery, #session

Hide A DOM Container Clicking Outside It Using jQuery

Let’s say we have a DIV container with some content in it. We want to hide that container if the user clicks outside its bounds. Pretty simple solution:

$( document ).mouseup( function( e ) {
    if( $( "#myDiv" ).has( e.target ).length === 0 ) {
        $( "#myDiv" ).hide();
    }
});

#click, #container, #div, #dom, #hide, #jquery

Programmatically Animated Page Scroll with jQuery

With the increasing use of AJAX to populate a page without reloading it, you come to find out that you’d like to get your users back to a certain point in the page, if not the top. If you where to navigate to a new page via the browser, you always end at the top. But when just replacing a container with new information this doesn’t happen. Depending upon your scenario, this may be a good idea for usability.

The following code snippet is a simple example. It will detect if your user is below 210 pixels on the screen. If you are then it will animate the page, scrolling it up to the 210px mark. You may want to use an extended jQuery .position() method to find the top of your #result container instead of using a static page height value.

$.get( 'ajax/test.html', function( data ) {
    $( '#result' ).html( data );

    if ( $( window ).scrollTop() > 210) {
        $( "html, body" ).animate({
            scrollTop: 210
        }, "slow" );
    }
});

#jquery, #scroll

Accessing JavaScript Methods From Outside Its Self-Invoking Function Wrapper

Let’s say you have a method you want to be globally accessible, but want a self-invoking function around it to make sure it’s jQuery $ alias safe.

In this example, the following code is in global.js:

(function( $ ) {
   function showLoader( cfg ) {
      if( cfg == 'show') {
         $( '.loader' ).show();
      } else //more code...
   }
})(jQuery);

In this example, the following code is in myPage.js:

(function($) {
   showLoader( 'show' );
})(jQuery);

You will find that myPage.js cannot access the showLoader() method even though global.js was called first in the HTML page.

The fix for this is to push the showLoader() method out to the window (global) scope:

(function( $ ) {
   window.showLoader = function( cfg ) {
      if( cfg == 'show') {
         $( '.loader' ).show();
      } else //more code...
   }
})(jQuery);

By assigning the function to the window scope it is now globally accessible. Just remember that it is now no longer protected by the closure but it still has access to the jQuery $ alias.

jQuery Select All Checkboxes

I needed to come up with a generic way of checking all checkboxes that could be reused using jQuery. At first I thought about using the data- attribute to define which class of checkboxes to check. But then found a great way to just check all checkboxes that are located in their element container. (The .on method required jQuery 1.7)

HTML Example Code:

<div class="control-group">
<input type="checkbox" class="selAllChksInGroup"> All
<input type="checkbox" value="NE"> Nebraska
<input type="checkbox" value="FL"> Florida
</div>

JavaScript Code:

$(document).ready(function(){

$("input[type=checkbox].selAllChksInGroup").on("click.chkAll", function( event ){
    $(this).parents('.control-group:eq(0)').find(':checkbox').prop('checked', this.checked);
});

});

jQuery Galleriffic Plugin History Update

Using the Galleriffic version 2.0.1 you will find it incompatible with the 2010-09-11 jQuery History plugin. It currently only works with the 2009 history plugin version.

When you insert the pageload function and its calls into your HTML, update it to the following. Lines 3 and 15 are updated.

// Initialize history plugin.
 // The callback is called at once by present location.hash.
 $.history.init(pageload);

// set onlick event for buttons using the jQuery 1.3 live method
 $("a[rel='history']").live('click', function(e) {
 if (e.button != 0) return true;

 var hash = this.href;
 hash = hash.replace(/^.*#/, '');

// moves to a new page.
 // pageload is called at once.
 // hash don't contain "#", "?"
 $.history.load(hash);

return false;
 });

Then update jquery.galleriffic.js:
Line 495:

$.history.load(String(imageData.hash));

Line 868:

if (this.enableHistory && !$.history.init)