Words about stuff, and things related to stuff

Quick jQuery email address obfuscator

Here's a quick little jQuery script I created just now. Don't give your email address to spam bots! Instead, try this.

Markup:

view plain print about
1<a href="/contact"><span class="no-script">Contact Us</span><span class="obfuscate">com/mydomain//me</span></a>
(Use your email address in the format above, where your original address is me@mydomain.com)

Continued...

CKeditor options for Mura CMS (and other CK things)

Now that Mura has been using the new CKeditor (formerly FCKeditor) , I have been looking at the various configuration options available: 

http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html

These configuration options can be added to your styles.js.cfm , stored in the Mura file structure at
[siteid]/includes/themes/[theme]/js/editor/


Mura CMS / CKeditor paste as plain text by default

Any developer who uses a CMS with a wysiwyg text editor knows the frustrations of clients pasting content from MS Word and other word processing programs directly into the editor, complete with unwanted styles, and even markup elements, causing all sorts of problems on our otherwise perfectly-formatted pages.

Mura CMS uses the ubiquitous CKeditor (formerly FCK) with a number of features enabled by default, including a "Paste as Plain Text" button. If clients remembered to use this, we would be all set. But, they don't.

Fortunately, there's an easy one-line fix.
Just open up [siteid]\includes\themes\[theme]\js\editor\styles.js

and paste in this line

CKEDITOR.config.forcePasteAsPlainText = true;

That's it. All pasting of content should now be clean, with extra styles and markup removed, just as if you had clicked "paste as plain text" in the editor toolbar. 

Persist form values on reload w/ jQuery & ColdFusion

A designer friend presented me with a somewhatlong and complicated HTML form. It is a survey/contest entry for employees of a specific industry, with dozens of fields which include regular text inputs, text areas, and radio buttons.

My task is to create a routine to submit this form via e-mail and store the contents in the database. ColdFusion makes that easy with the built-in #form# scope. And for validation, I simply made his HTML < form > into a <cfform>, and all of the <input> and < textarea > elements into <cfinput> and <cftextarea>, adding 'required="true"' and a validation rule and/or message for each. Whipping up a quick server-side validation for the required fields was also a snap with CF.

Then it occurred to me - if there is in fact some error with the user submission, we will return the user to the form showing an error message at the top of the page.

However, unless we somehow persist the values that were entered, the form will be blank, and the user will no doubt be a little frustrated. This is a long form!

one method would be to create <cfparam> values for each element in the form, and then give each input the a value like value='#form.thisFieldName#" - that's common practice, and works great, but I don't want to do the tedious work. Also, I will be passing this form back to the designer, and don't want him to have to create the default value if he decides to change the names of any inputs, or at other input to the form.

So, I came up with this.

Assuming you already have jQuery in your page, this little block of code will loop through all of the posted values in the ColdFusion form scope, then, using jQuery, will assign the given value to any form element with a matching "name" attribute, allowing us to easily re-populate all the form fields in the page, in one fell swoop with javascript.

Since i only had input / textarea / select / radio button inputs, this just handles those types, but could easily be ammended to include checkboxes and more.

<!--- if form scope exists --->
<cfif isDefined('form.fieldnames')>
    <cfsavecontent variable="hcode">
    <script type="text/javascript">
    $(document).ready(function(){
    <cfloop list="#form.fieldNames#" index="ff">
    <cfset rawVal = form[ff]>
    <cfset ffVal = jsStringFormat(form[ff])>
    <cfoutput>
    $('input[type="text"][name="#lcase(ff)#"]').val('#ffval#');
    $('select[name="#lcase(ff)#"]').val('#ffval#');
    $('textarea[name="#lcase(ff)#"]').text('#ffval#');
    $('input[type="radio"][name="#lcase(ff)#"][value="#rawval#"]').attr('checked','checked');
    </cfoutput>
    </cfloop>
    //end jQuery
    });
    </script>
    </cfsavecontent>
<cfhtmlhead text="#hcode#">
</cfif>


Yes, this makes a big long looped jQuery script in the head of the user's page. But in this instance, it is working perfectly, with no noticeable slowing of page load, or other drawbacks that i can see.

jQuery Masked Input: change masking based on a selected value with unmask()

Along with standard jQuery form validation, the jQuery masked input plugin provides a really nice way to preformat user-entered text. For example, a phone number might be masked so that it always ends up in the format "(999) 555-1234", even if the user just types in 9995551234 .

There are a number of ways this can be applied to a form, including the formatting of credit card numbers. Along with jQuery's validation methods for minlength and maxlength, you may also want to make sure the values entered are numeric only, or perhaps break up the user-entered data into hyphenated blocks, i.e. "9999-1234-1234-1234". This works really well and from what I can tell, helps cut down on errors due to mistyped card numbers.

However, not all credit cards use the same format. Amex is 13, Visa and others 16... and I believe there are some 14 or 15 digit variations.  The brilliant and thoughtful masked input plugin provides two solutions to this issue:

1) Variable (flexible) masking
With a recent release, you can now add a ? character to imply optional trailing digits
So, for an input that should accept 13-16 characters, like a credit card number:
    $('#cc-number').mask("9999999999999?999");
The same works for the CCV code, which can be either 3 or 4 digits
    $('#sec-code').mask("999?9");

2) Change the mask based on a user selection
Also introduced in a recent version, we now have the "unmask()" function.

So, to change the masking format based on the selection of another field - in this case, the type of credit card - is very easy.

In my form, I have a select box with the id of #cardType, which allows the user to select the type of card, and the options have values such as 'visa,amex,disc', etc.

To adjust the masking based on the user's card type, we simply need to grab the value of the dropdown when it is changed - if the value is 'amex' , set the mask for 13 digits, and 4 for the ccv (amex standard). If otherwise, set it to 16 and 3 digits. }

Here's my code, using unmask():

    // change mask for cc type
    $('#cardType').change(function(){
        var ctype = $(this).find('option:selected').attr('value');
        if(ctype=='amex'){
            // amex masking
            $('#
cc-number').unmask().mask("9999999999999");
            $('#
sec-code').unmask().mask("9999");
        } else {
            // standard masking
            $('#
cc-number').unmask().mask("9999999999999999");
            $('#
sec-code').unmask().mask("999");
        }
    });



Once again a superbly elegant and simple solution via jQuery.
From here, you can easily edit these rules to allow for specific masks based on other values.

 

 

 

jQuery AJAX get() function cached in IE

From the 'how the heck were we supposed to know this?' department, a little tidbit I'd like to share, perhaps saving some poor, frustrated developer from the same type of mystifying madness:

jQuery "get()" operations are cached in IE unless you pass in a unique URL !

By cached, I mean, you can have a remote page with a query or other function, which responds to a unique set of values being passed in, and it will work fine in Firefox, but in IE, you'll get the same results from the remote page, even if the response is unique (like query results that have changed, for the same record), unless the values you are passing in via get() are also unique (i.e. you haven't looked up that particular record yet).

So, before you go banging your head on the floor and screaming out 'why oh why can't i find the problem, let alone the solution?!?' (not that i would ever do that), read this post, which i found via a quick Google search for 'jquery get IE cache' : http://www.sitecrafting.com/blog/ajax-ie-caching-issues/ 

I only had the presence of mind to run that search once i got my ajax results to display as an alert on the calling page, and deduced that the displayed content was based on the first values passed in, while all other updates and requests were ignored.

The fix, as that post suggests, is to create a unique url. In this case, i use javascript to append a random number to my function, - in this case, I just use javascript to create a random number right inline, then add that as a meaningles url variable to the page in the get() function:

                           // set up unique url
                            var randomNo = Math.floor(Math.random()*9999999);
                            var urlStr = 'journalLookup.cfm?r=' + randomNo;
                            // run get function using randomized URL
                            $.get(urlStr,{jdate:dateLookup}, function(data){  .... [ function here ]

If this saves you some aggravation, let me know in the comments ... maybe i'll feel better about the time i just wasted chasing this mystery 'feature' in IE!!

 

New TinyMCE lets you paste as plain text automatically

When building web applications, one of the most common functions involves allowing a site administrator to add or edit content on a page via some sort of 'wysiwyg' (what you see is what you get) editor. My tool of choice for this purpose is the very versatile, relatively light TinyMCE ( tinymce.moxiecode.com)

As great as these tools are, it is inevitable that the average site administrator will want to copy and paste some text from a word processing function like MS Word or WordPerfect, both of which add invisible formatting to the copy-and-pasted text, which in turn can really mess up even the best of web designs.

TinyMCE has had a very nice 'paste from word' plugin for as long as I can remember, but it still requires the user to click a tiny icon for the 'paste from word' popup, and then paste their text into that. ( There is also a 'paste as plain text' option which removes all formatting just as if you'd copied into notepad first, then pasted into the web form). So, even with these great options, I still find myself answering more than a few support calls related to text not looking quite like it should (or worse) when copying from Word.

Much to my delight, I've found a much better solution - the newest version of TinyMCE has the "paste" plugin built in, and it now includes options for cleaning up the text automatically when pasting right into the main text box (without needing to use the littlepopup icons).

As seen on the paste plugin page (TinyMCE:Plugins/paste - Moxiecode Documentation Wiki) you simply need to include 'paste' in the list of plugins when initializing the tinyMCE script, and then can call the cleanup options like this:

            paste_auto_cleanup_on_paste : true,
            paste_remove_styles: true,
            paste_remove_styles_if_webkit: true,
            paste_strip_class_attributes: true,

This runs the 'paste from word' function automatically, then removes any remaining inline style and class attributes , leaving you with nice clean paragraph-formatted html.

so, whether your clients remember to use that little button or not, whatever they paste (via ctrl+v on their keyboards) will be stripped clean, ready to be shown in pristine valid html format on your site.

How to show changed input and select fields with jQuery

Here are a few quick easy jquery functions that will add a class of 'changed' to any input or select box if it is changed, and will remove that class if the value is reset to the default.

view plain print about
1// inputs
2        $('form.myForm :input').change(function(){
3        var defval = $(this)[0].defaultValue;
4        if ($(this).val()!=defval){
5            $(this).addClass('changed');
6        } else {
7             $(this).removeClass('changed');
8        }
9        }).keyup(function(){
10             $(this).trigger('change');
11        });
12
13        // select boxes
14        $('form.myForm select').change(function(){
15        var defval = $(this).find('option[defaultSelected=true]').val();
16        if ($(this).val()!=defval){
17            $(this).addClass('changed');
18        } else {
19             $(this).removeClass('changed');
20        }
21        }).keyup(function(){
22             $(this).trigger('change');
23        });

Note in both cases we use keyup (onkeyup) to trigger the 'change' event. This allows the browser to show the changed class as soon as the user changes the value using a keyboard, rather than 'onblur'.

Javascript How To: Add a Number of Days to a Date

I have a form where a user can input a date in the format mm/dd/yyyy, and we need to add an 'expiration date' that is 30 days from the date entered, regardless of calendar month, leap years, or other complex calculations.

Thankfully, javascript already has the 'setDate()' function built in. We just have to parse the date object (a full javascript formatted date string) out of the given form input ( e.g. '11/10/2009').

Easy enough - first we parse out the numeric date/time object, and in this example, write it to the screen.

Then, we add 30 days (the number of days can be changed in the setting for the 'interval' variable - use a negative number to remove days)

Again, this example simply writes the date to the screen. In the current usage, I will use that date value to populate another text input in the same form as the initial date being entered.

view plain print about
1<script type="text/javascript">
2function setExpDate(formDate){
3    // set number of days to add
4    var interval = 30;
5    var startDate = new Date(Date.parse(formDate));
6    document.write('start: ' + startDate);
7    var expDate = startDate;
8    expDate.setDate(startDate.getDate() + interval);
9    document.write('<br>expire: ' + expDate);
10};
11</script>

Example usage on a form input:

view plain print about
1<input type="text" size="10" maxlength="10" id="startDate" name="startDate" onblur="setExpDate(this.value)">   

jQuery Cycle slideshow demo with next/back/paging links

This demo uses some of the advanced features of the jQuery Cycle plugin to create a mixed content slideshow (html/text/images) with a visual navigation bar, and next/back arrows

In this setup, the slideshow advances automatically, until somebody clicks one of the links, which stops the automatic action and allows the user to control the advancement of the slides. The Cycle plugin allows for pausing, resuming or stopping the slideshow with a simple click action.

See the demo (includes css/jquery code for easy reuse) http://www.gowestwebdesign.com/demos/jquery-cycle-gallery-next-back-paging/

Thanks to Mike Alsup for the versatile Cycle plugin: http://www.malsup.com/jquery/cycle/

Feel free to use the code for any purpose, and please drop me a comment or link to check out your work. As always, comments/suggestions/fixes are welcome.

Replace missing images sitewide with jQuery and ColdFusion

In my application.cfc, within the 'onRequestStart' function, I have a variable defined for the replacement image to use in my site.

Then, in an include that puts special goodies into the head of every generated html page sitewide, I have this bit of cf code

view plain print about
1<!--- missing img src --->
2<cfif fileExists(expandPath(request.missingImage))>
3    <cfset imgErrorSrc = '#request.thisdir##request.missingImage#'>
4<cfelse>
5    <cfset imgErrorSrc = ''>
6</cfif>

This checks to make sure the 'error' image actually exists - if not, we just use a blank '' value. (Note: request.thisDir is my own global variable that references the root of the site - you can replace that with any hardcoded or dynamic url)

Then, write a simple function to replace any images - this duplicates a similar usage of the tag's 'onerror' attribute ( which is fine for images in non-strict doctypes, but is not entirely valid ).

view plain print about
1<!--- replace all missing images with replacement image, or with empty src --->
2<script type="text/javascript">
3$(document).ready(function(){
4    $('body img').error(function(){
5        $(this).attr('src', '<cfoutput>#imgErrorSrc#</cfoutput>').attr('alt','');
6    });
7});
8</script>

That's it. Also notice I am clearing the 'alt' attribute as a final precaution. Using this method, any missing image anywhere within the html will simply be 'blanked out'.

I could use jQuery's .remove() function, to remove a missing image completely from the DOM, but there are times when I want the space or layout to be preserved, and really, I don't want errant images in my pages at all, so this gives me a way to debug if I find my self (inevitably) scratching my head when a missing image simply 'vanishes' from the page 4 or 5 sites from now.

As a final note, the CF part of this, setting the value for the 'src' attribute, can be enhanced to include different images for different pages, or under different conditions. Likewise, the jQuery function could be expanded to target images in one location with a specific action, and in another with a separate function by using different css selectors.

Just another example of how Jquery + CF = bliss :-)

Read More...

Recent Comments

Filezilla - open site manager or a specific site connection on startup
ggibby said: Took me some tinkering to discover that there should be NO quotes around "0/myftpsite" May... [More]

Add content to a category in Mura content bean, and how to see other methods
Tony said: Thanks Simon, I'm new to MURA and that saved me a lot of time. Don't know if this will help anyone b... [More]

Download CFQuery results as Excel with CFspreadsheet
Seth said: You can use CFCONTENT to set the MIME type and avoid the warning message. You also don't need to wri... [More]

Add index.cfm as default doucment in .htaccess
Michael Evangelista said: Hi Alex, you can use the web.config or the .htaccess file. But in Mura, as far as I know you would a... [More]

Feed Unavailable