/*!
* Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
*
* This Software is licenced under the LGPL Licence (GNU Lesser General
* Public License).
* In addition to the LGPL Licence the Software is subject to the
* following conditions:
*
* i every modification must be public and comunicated to the Author
* ii every "jsbean" added to this library must be self consistent
* except for the dependence from jsbeans-x.x.x.js
* iii copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* EditInLine turns non-editable elements, such as DIVs or SPANs, into input text, textarea or select on the 'onclick' event, so they can be editable.<br/>
* On the 'onblur' event the element turns to be non-editable, but with the new value.
* @namespace jsbeans
* @class EditInLine
* @static
*/
jsbeans.EditInLine = {
/**
* Turns editable a DOM Object using its id. Operates over multiple Objects if {@param} <code class="param">ids</code> is an Array of Strings.
* @method byId
* @param ids {String | Array<String>}
* @param [options] {Array | Function | JSON} may be an Array of values, a callback (fired on the 'onblur' event), or a JSON with all of them.<br/>
* <pre>
* onBeforeEnter: <Function>, // optional
* onEnter: <Function>, // optional
* onExit: <Function>, // optional
* onBlur: <Function>, // optional. Overrides default behaviour.
* values: <Array> // optional list of values. It automatically turns element to a Select.
* mode: <String> // by now only 'textarea' is admitted. It automatically turns element to a Textarea.
* cols: <Integer> // number of columns of the textarea used as textarea's cols attribute. Used only if 'mode' option has 'textarea' value.
* rows: <Array> // number of rows of the textarea used as textarea's rows attribute. Used only if 'mode' option has 'textarea' value.
* </pre>
* @static
* */
byId: function(ids /*, options*/) {
var opts = jsbeans.EditInLine._getOptions(arguments[1], arguments[2]);
if (jsbeans.EditInLine._isArr(ids)) {
for (var i = 0, id; id = ids[i]; i++) {
var o = document.getElementById(id);
if (typeof o == "undefined" || o == null) {
continue;
}
else {
jsbeans.EditInLine._makeEditable(o, opts);
}
}
}
else {
var o = document.getElementById(ids);
if (typeof o == "undefined" || o == null) {
return;
}
else {
jsbeans.EditInLine._makeEditable(o, opts);
}
}
},
/**
* Turns editable DOM Objects selected by their style class.<br/>
* Everything else works just like <code class="methd">byId</code>.
* @method byClassname
* @param class_name {String} the style class for selection
* @param [options] {Array | Function | JSON}
* @static
* */
byClassname: function(className /*, options*/) {
var opts = jsbeans.EditInLine._getOptions(arguments[1], arguments[2]);
var classNameRE = new RegExp('\\b' + className + '\\b');
var els = document.getElementsByTagName('*');
for (var i = 0, o; o = els[i]; i++) {
if (classNameRE.test(o.className)) {
jsbeans.EditInLine._makeEditable(o, opts);
}
}
},
/**
* As <code class="methd">byClassName</code>, but with a different case.
* @method byClassName
* @param class_name {String}
* @param options {Array | Function | JSON}
* @static
* */
byClassName: function(cn /*, options*/) {
jsbeans.EditInLine.byClassname(cn, arguments[1]);
},
/**
* @method _makeEditable
* @param object {DOM} the DOM Object that will be editable
* @param options {JSON} functions <code>onBeforeEnter</code>, <code>onEnter</code> and <code>onExit</code> and <code>values</code> for options.
* @private
* @static
* */
_makeEditable: function(o, options) {
o.onclick = function() {
var input;
var content = o.innerHTML == " " ? "" : o.innerHTML;
var values = options.values;
if (values != null) {
input = document.createElement("select");
var index = 0;
for (var i = 0, val; val = values[i]; i++) {
var opt = document.createElement("option");
opt.text = val;
opt.innerText = val;
opt.value = val;
input.appendChild(opt);
if (content == val) {
index = i;
}
}
input.selectedIndex = index;
}
else if (options.mode && options.mode == "textarea") {
input = document.createElement("textarea");
input.setAttribute("cols", options.cols ? options.cols : "50");
input.setAttribute("rows", options.rows ? options.rows : "20");
input.value = content;
}
else {
input = document.createElement("input");
input.setAttribute("type", "text");
input.setAttribute("size", content.length + 1);
input.value = content;
}
input.className = "jsbeans_EditInLine_input";
if (o.id) {
input.setAttribute("id", o.id + "_input");
}
input.onblur = function() {
// override default behaviour
if (options.onBlur != null) {
options.onBlur(o);
}
else {
o.innerHTML = input.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') == "" ? " " : input.value;
input.parentNode.replaceChild(o, input);
input = null;
}
// always invoke onExit, if defined
if (options.onExit != null) {
options.onExit(o);
}
};
if (options.onBeforeEnter != null) {
options.onBeforeEnter(o);
}
o.parentNode.replaceChild(input, o);
if (options.onEnter != null) {
options.onEnter(input);
}
input.focus();
if (input.getAttribute("type") == "text" || input.getAttribute("cols") != null) {
input.select();
}
};
},
/**
* @method _getOptions
* @param arg1 {Array | Function | JSON}
* @param arg2 {Array | Function | JSON}
* @private
* @static
* */
_getOptions: function(arg1, arg2) {
var values = null;
var onEnter = null;
var onBeforeEnter = null;
var onExit = null;
var onBlur = null;
var mode, cols, rows = null;
if (typeof arg1 != "undefined") {
if (jsbeans.EditInLine._isArr(arg1)) {
values = arg1;
}
else if (jsbeans.EditInLine._isFun(arg1)) {
onExit = arg1;
}
else if (arg1.values || arg1.onBeforeEnter || arg1.onEnter || arg1.onExit || arg1.onBlur) {
if (arg1.values) {
values = arg1.values;
}
if (arg1.onEnter && jsbeans.EditInLine._isFun(arg1.onEnter)) {
onEnter = arg1.onEnter;
}
if (arg1.onBeforeEnter && jsbeans.EditInLine._isFun(arg1.onBeforeEnter)) {
onBeforeEnter = arg1.onBeforeEnter;
}
if (arg1.onExit && jsbeans.EditInLine._isFun(arg1.onExit)) {
onExit = arg1.onExit;
}
if (arg1.onBlur && jsbeans.EditInLine._isFun(arg1.onBlur)) {
onBlur = arg1.onBlur;
}
}
if (arg1.mode) {
mode = arg1.mode;
}
if (arg1.cols) {
cols = arg1.cols;
}
if (arg1.rows) {
rows = arg1.rows;
}
}
var opts = {
values: values,
onBeforeEnter: onBeforeEnter,
onEnter: onEnter,
onExit: onExit,
onBlur: onBlur,
mode: mode,
cols: cols,
rows: rows
};
if (arg2 && jsbeans.EditInLine._isFun(arg2)) {
opts.onExit = arguments[2];
}
else if (arg1 && jsbeans.EditInLine._isFun(arg1)) {
opts.onExit = arg1;
}
return opts;
},
/**
* Cheks if fn is a Function
* @method _isFun
* @param func {Object}
* @return {boolean} true if <code class="param">func</code> is a <code>Function</code>
* @private
* @static
* */
_isFun: function(fn) {
return !!fn && typeof fn != "string" && !fn.nodeName &&
fn.constructor != Array && /function/i.test( fn + "" );
},
/**
* Checks if a is an Array
* @method _isArr
* @param arr {Object}
* @return {boolean} true if <code class="param">arr</code> is an <code>Array</code>
* @private
* @static
* */
_isArr: function(a) {
return (typeof a == "object" && a.constructor == Array);
}
};