/* +-----------------------------------------------------------------------+ | Copyright (c) 2006-2007 Mika Tuupola, Dylan Verheul | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions | | are met: | | | | o Redistributions of source code must retain the above copyright | | notice, this list of conditions and the following disclaimer. | | o Redistributions in binary form must reproduce the above copyright | | notice, this list of conditions and the following disclaimer in the | | documentation and/or other materials provided with the distribution.| | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | | +-----------------------------------------------------------------------+ */ /* $Id$ */ /** * jQuery inplace editor plugin (version 1.3.x) * * Based on editable by Dylan Verheul * http://www.dyve.net/jquery/?editable * * @name jEditable * @type jQuery * @param String target POST URL or function name to send edited content * @param Hash options additional options * @param String options[name] POST parameter name of edited content * @param String options[id] POST parameter name of edited div id * @param String options[type] text, textarea or select * @param Integer options[rows] number of rows if using textarea * @param Integer options[cols] number of columns if using textarea * @param Mixed options[height] 'auto' or height in pixels * @param Mixed options[width] 'auto' or width in pixels * @param String options[loadurl] URL to fetch content before editing * @param String options[loadtype] Request type for load url. Should be GET or POST. * @param String options[data] Or content given as paramameter. * @param String options[indicator] indicator html to show when saving * @param String options[tooltip] optional tooltip text via title attribute * @param String options[event] jQuery event such as 'click' of 'dblclick' * @param String options[onblur] 'cancel', 'submit' or 'ignore' * @param String options[submit] submit button value, empty means no button * @param String options[cancel] cancel button value, empty means no button * @param String options[cssclass] CSS class to apply to input form. 'inherit' to copy from parent. * @param String options[style] Style to apply to input form 'inherit' to copy from parent. * @param String options[select] true or false, when true text is highlighted * */ jQuery.fn.editable = function(target, options, callback) { /* prevent elem has no properties error */ if (this.length == 0) { return(this); }; var settings = { target : target, name : 'value', id : 'id', type : 'text', width : 'auto', height : 'auto', event : 'click', onblur : 'cancel', loadtype : 'GET' }; if(options) { jQuery.extend(settings, options); }; var callback = callback || function() { }; jQuery(this).attr('title', settings.tooltip); jQuery(this)[settings.event](function(e) { /* save this to self because this changes when scope changes */ var self = this; /* prevent throwing an exeption if edit field is clicked again */ if (self.editing) { return; } /* figure out how wide and tall we are */ var width = ('auto' == settings.width) ? jQuery(self).width() : settings.width; var height = ('auto' == settings.height) ? jQuery(self).height() : settings.height; self.editing = true; self.revert = jQuery(self).html(); self.innerHTML = ''; /* create the form object */ var f = document.createElement('form'); /* apply css or style or both */ if (settings.cssclass) { if ('inherit' == settings.cssclass) { jQuery(f).attr('class', jQuery(self).attr('class')); } else { jQuery(f).attr('class', settings.cssclass); } } if (settings.style) { if ('inherit' == settings.style) { jQuery(f).attr('style', jQuery(self).attr('style')); /* IE needs the second line or display wont be inherited */ jQuery(f).css('display', jQuery(self).css('display')); } else { jQuery(f).attr('style', settings.style); } } /* main input element */ var i; switch (settings.type) { case 'textarea': i = document.createElement('textarea'); if (settings.rows) { i.rows = settings.rows; } else { jQuery(i).height(height); } if (settings.cols) { i.cols = settings.cols; } else { jQuery(i).width(width); } break; case 'select': i = document.createElement('select'); break; default: i = document.createElement('input'); i.type = settings.type; jQuery(i).width(width); jQuery(i).height(height); /* https://bugzilla.mozilla.org/show_bug.cgi?id=236791 */ i.setAttribute('autocomplete','off'); } /* maintain bc with 1.1.1 and earlier versions */ if (settings.getload) { settings.loadurl = settings.getload; settings.loadtype = 'GET'; } else if (settings.postload) { settings.loadurl = settings.postload; settings.loadtype = 'POST'; } /* set input content via POST, GET, given data or existing value */ if (settings.loadurl) { var data = {}; data[settings.id] = self.id; jQuery.ajax({ type : settings.loadtype, url : settings.loadurl, data : data, success: function(str) { setInputContent(str); } }); } else if (settings.data) { setInputContent(settings.data); } else { setInputContent(self.revert); } i.name = settings.name; f.appendChild(i); if (settings.submit) { var b = document.createElement('input'); b.type = 'submit'; b.value = settings.submit; f.appendChild(b); } if (settings.cancel) { var b = document.createElement('input'); b.type = 'button'; b.value = settings.cancel; jQuery(b).click(function() { reset(); }); f.appendChild(b); } /* add created form to self */ self.appendChild(f); i.focus(); /* highlight input contents when requested */ if (settings.select) { i.select(); } /* discard changes if pressing esc */ jQuery(i).keydown(function(e) { if (e.keyCode == 27) { e.preventDefault(); reset(); } }); /* discard, submit or nothing with changes when clicking outside */ /* do nothing is usable when navigating with tab */ var t; if ('cancel' == settings.onblur) { jQuery(i).blur(function(e) { t = setTimeout(reset, 500) }); } else if ('submit' == settings.onblur) { jQuery(i).blur(function(e) { jQuery(f).submit(); }); } else { jQuery(i).blur(function(e) { /* TODO: maybe something here */ }); } jQuery(f).submit(function(e) { if (t) { clearTimeout(t); } /* do no submit */ e.preventDefault(); /* check if given target is function */ if (jQuery.isFunction(settings.target)) { var str = settings.target.apply(self, [jQuery(i).val(), settings]); self.innerHTML = str; self.editing = false; callback.apply(self, [self.innerHTML, settings]); } else { /* add edited content and id of edited element to POST */ var p = {}; p[i.name] = jQuery(i).val(); p[settings.id] = self.id; /* show the saving indicator */ jQuery(self).html(settings.indicator); jQuery.post(settings.target, p, function(str) { self.innerHTML = str; self.editing = false; callback.apply(self, [self.innerHTML, settings]); }); } return false; }); function reset() { self.innerHTML = self.revert; self.editing = false; }; function setInputContent(str) { if (jQuery.isFunction(str)) { var str = str.apply(self, [self.revert, settings]); } switch (settings.type) { case 'select': if (String == str.constructor) { eval ("var json = " + str); for (var key in json) { if ('selected' == key) { continue; } o = document.createElement('option'); o.value = key; var text = document.createTextNode(json[key]); o.appendChild(text); if (key == json['selected']) { o.selected = true; } i.appendChild(o); } } break; default: i.value = str; break; } } }); return(this); };