web/data/cubicweb.compat.js
author Alexandre Fayolle <alexandre.fayolle@logilab.fr>
Sat, 29 May 2010 10:33:40 +0200
branchstable
changeset 5607 38d43dc5ee57
parent 5186 f3c2cb460ad9
child 5658 7b9553a9db65
permissions -rw-r--r--
skip flymake junk when reloading this avoids nasty ex aequo scores in selectors in dev. mode

/* MochiKit -> jQuery compatibility module */

function forEach(array, func) {
    for (var i=0, length=array.length; i<length; i++) {
	func(array[i]);
    }
}

// XXX looks completely unused (candidate for removal)
function getElementsByTagAndClassName(tag, klass, root) {
    root = root || document;
    // FIXME root is not used in this compat implementation
    return jQuery(tag + '.' + klass);
}

/* jQUery flattens arrays returned by the mapping function:
   >>> y = ['a:b:c', 'd:e']
   >>> jQuery.map(y, function(y) { return y.split(':');})
   ["a", "b", "c", "d", "e"]
   // where one would expect:
   [ ["a", "b", "c"], ["d", "e"] ]
   XXX why not the same argument order as $.map and forEach ?
*/
function map(func, array) {
    var result = [];
    for (var i=0, length=array.length;
         i<length;
         i++) {
	result.push(func(array[i]));
    }
    return result;
}

function findValue(array, element) {
    return jQuery.inArray(element, array);
}

function filter(func, array) {
    return jQuery.grep(array, func);
}

function noop() {}

function addElementClass(node, klass) {
    jQuery(node).addClass(klass);
}

// XXX looks completely unused (candidate for removal)
function toggleElementClass(node, klass) {
    jQuery(node).toggleClass(klass);
}

function removeElementClass(node, klass) {
    jQuery(node).removeClass(klass);
}

hasElementClass = jQuery.className.has;


function partial(func) {
    var args = sliceList(arguments, 1);
    return function() {
	return func.apply(null, merge(args, arguments));
    };
}


function log() {
    // XXX dummy implementation
    // console.log.apply(arguments); ???
    var args = [];
    for (var i=0; i<arguments.length; i++) {
	args.push(arguments[i]);
    }
    if (typeof(window) != "undefined" && window.console
        && window.console.log) {
	window.console.log(args.join(' '));
    }
}

function getNodeAttribute(node, attribute) {
    return jQuery(node).attr(attribute);
}

function isArray(it){ // taken from dojo
    return it && (it instanceof Array || typeof it == "array");
}

function isString(it){ // taken from dojo
    return !!arguments.length && it != null && (typeof it == "string" || it instanceof String);
}


function isArrayLike(it) { // taken from dojo
    return (it && it !== undefined &&
	    // keep out built-in constructors (Number, String, ...) which have length
	    // properties
	    !isString(it) && !jQuery.isFunction(it) &&
	    !(it.tagName && it.tagName.toLowerCase() == 'form') &&
	    (isArray(it) || isFinite(it.length)));
}


function getNode(node) {
    if (typeof(node) == 'string') {
        return document.getElementById(node);
    }
    return node;
}

/* safe version of jQuery('#nodeid') because we use ':' in nodeids
 * which messes with jQuery selection mechanism
 */
function jqNode(node) {
    node = getNode(node);
    if (node) {
	return jQuery(node);
    }
    return null;
}

function evalJSON(json) { // trust source
    return eval("(" + json + ")");
}

function urlEncode(str) {
    if (typeof(encodeURIComponent) != "undefined") {
        return encodeURIComponent(str).replace(/\'/g, '%27');
    } else {
        return escape(str).replace(/\+/g, '%2B').replace(/\"/g,'%22').rval.replace(/\'/g, '%27');
    }
}

function swapDOM(dest, src) {
    dest = getNode(dest);
    var parent = dest.parentNode;
    if (src) {
        src = getNode(src);
        parent.replaceChild(src, dest);
    } else {
        parent.removeChild(dest);
    }
    return src;
}

function replaceChildNodes(node/*, nodes...*/) {
    var elem = getNode(node);
    arguments[0] = elem;
    var child;
    while ((child = elem.firstChild)) {
        elem.removeChild(child);
    }
    if (arguments.length < 2) {
        return elem;
    } else {
	for (var i=1; i<arguments.length; i++) {
	    elem.appendChild(arguments[i]);
	}
	return elem;
    }
}

update = jQuery.extend;


function createDomFunction(tag) {

    function builddom(params, children) {
	var node = document.createElement(tag);
	for (key in params) {
	    var value = params[key];
	    if (key.substring(0, 2) == 'on') {
		// this is an event handler definition
		if (typeof value == 'string') {
		    // litteral definition
		    value = new Function(value);
		}
		node[key] = value;
	    } else { // normal node attribute
		jQuery(node).attr(key, params[key]);
	    }
	}
	if (children) {
	    if (!isArrayLike(children)) {
		children = [children];
		for (var i=2; i<arguments.length; i++) {
		    var arg = arguments[i];
		    if (isArray(arg)) {
			children = merge(children, arg);
		    } else {
			children.push(arg);
		    }
		}
	    }
	    for (var i=0; i<children.length; i++) {
		var child = children[i];
		if (typeof child == "string" || typeof child == "number") {
		    child = document.createTextNode(child);
		}
		node.appendChild(child);
	    }
	}
	return node;
    }
    return builddom;
}

A = createDomFunction('a');
BUTTON = createDomFunction('button');
BR = createDomFunction('br');
CANVAS = createDomFunction('canvas');
DD = createDomFunction('dd');
DIV = createDomFunction('div');
DL = createDomFunction('dl');
DT = createDomFunction('dt');
FIELDSET = createDomFunction('fieldset');
FORM = createDomFunction('form');
H1 = createDomFunction('H1');
H2 = createDomFunction('H2');
H3 = createDomFunction('H3');
H4 = createDomFunction('H4');
H5 = createDomFunction('H5');
H6 = createDomFunction('H6');
HR = createDomFunction('hr');
IMG = createDomFunction('img');
INPUT = createDomFunction('input');
LABEL = createDomFunction('label');
LEGEND = createDomFunction('legend');
LI = createDomFunction('li');
OL = createDomFunction('ol');
OPTGROUP = createDomFunction('optgroup');
OPTION = createDomFunction('option');
P = createDomFunction('p');
PRE = createDomFunction('pre');
SELECT = createDomFunction('select');
SPAN = createDomFunction('span');
STRONG = createDomFunction('strong');
TABLE = createDomFunction('table');
TBODY = createDomFunction('tbody');
TD = createDomFunction('td');
TEXTAREA = createDomFunction('textarea');
TFOOT = createDomFunction('tfoot');
TH = createDomFunction('th');
THEAD = createDomFunction('thead');
TR = createDomFunction('tr');
TT = createDomFunction('tt');
UL = createDomFunction('ul');

// cubicweb specific
//IFRAME = createDomFunction('iframe');
function IFRAME(params){
  if ('name' in params){
    try {
      var node = document.createElement('<iframe name="'+params['name']+'">');
    } catch (ex) {
      var node = document.createElement('iframe');
      node.id = node.name = params.name;
    }
  }
  else{
    var node = document.createElement('iframe');
  }
  for (key in params) {
    if (key != 'name'){
      var value = params[key];
      if (key.substring(0, 2) == 'on') {
	// this is an event handler definition
	if (typeof value == 'string') {
	  // litteral definition
	  value = new Function(value);
	}
	node[key] = value;
      } else { // normal node attribute
	node.setAttribute(key, params[key]);
      }
    }
  }
  return node;
}


// dummy ultra minimalist implementation on deferred for jQuery
function Deferred() {
    this.__init__(this);
}

jQuery.extend(Deferred.prototype, {
    __init__: function() {
	this._onSuccess = [];
	this._onFailure = [];
	this._req = null;
        this._result = null;
        this._error = null;
    },

    addCallback: function(callback) {
        if (this._req.readyState == 4) {
            if (this._result) { callback.apply(null, this._result, this._req); }
        }
        else { this._onSuccess.push([callback, sliceList(arguments, 1)]); }
	return this;
    },

    addErrback: function(callback) {
        if (this._req.readyState == 4) {
            if (this._error) { callback.apply(null, this._error, this._req); }
        }
        else { this._onFailure.push([callback, sliceList(arguments, 1)]); }
	return this;
    },

    success: function(result) {
        this._result = result;
	try {
	    for (var i=0; i<this._onSuccess.length; i++) {
		var callback = this._onSuccess[i][0];
		var args = merge([result, this._req], this._onSuccess[i][1]);
		callback.apply(null, args);
	    }
	} catch (error) {
	    this.error(this.xhr, null, error);
	}
    },

    error: function(xhr, status, error) {
        this._error = error;
	for (var i=0; i<this._onFailure.length; i++) {
	    var callback = this._onFailure[i][0];
	    var args = merge([error, this._req], this._onFailure[i][1]);
	    callback.apply(null, args);
	}
    }

});


/*
 * Asynchronously load an url and return a deferred
 * whose callbacks args are decoded according to
 * the Content-Type response header
 */
function loadRemote(url, data, reqtype) {
    var d = new Deferred();
    jQuery.ajax({
	url: url,
	type: reqtype,
	data: data,

	beforeSend: function(xhr) {
	    d._req = xhr;
	},

	success: function(data, status) {
            if (d._req.getResponseHeader("content-type") == 'application/json') {
              data = evalJSON(data);
            }
	    d.success(data);
	},

	error: function(xhr, status, error) {
          try {
            if (xhr.status == 500) {
                var reason_dict = evalJSON(xhr.responseText);
                d.error(xhr, status, reason_dict['reason']);
                return;
            }
          } catch(exc) {
            log('error with server side error report:' + exc);
          }
          d.error(xhr, status, null);
	}
    });
    return d;
}


/** @id MochiKit.DateTime.toISOTime */
toISOTime = function (date, realISO/* = false */) {
    if (typeof(date) == "undefined" || date === null) {
        return null;
    }
    var hh = date.getHours();
    var mm = date.getMinutes();
    var ss = date.getSeconds();
    var lst = [
        ((realISO && (hh < 10)) ? "0" + hh : hh),
        ((mm < 10) ? "0" + mm : mm),
        ((ss < 10) ? "0" + ss : ss)
    ];
    return lst.join(":");
};

_padTwo = function (n) {
    return (n > 9) ? n : "0" + n;
};

/** @id MochiKit.DateTime.toISODate */
toISODate = function (date) {
    if (typeof(date) == "undefined" || date === null) {
        return null;
    }
    return [
        date.getFullYear(),
        _padTwo(date.getMonth() + 1),
        _padTwo(date.getDate())
    ].join("-");
};


/** @id MochiKit.DateTime.toISOTimeStamp */
toISOTimestamp = function (date, realISO/* = false*/) {
    if (typeof(date) == "undefined" || date === null) {
        return null;
    }
    var sep = realISO ? "T" : " ";
    var foot = realISO ? "Z" : "";
    if (realISO) {
        date = new Date(date.getTime() + (date.getTimezoneOffset() * 60000));
    }
    return toISODate(date) + sep + toISOTime(date, realISO) + foot;
};



/* depth-first implementation of the nodeWalk function found
 * in MochiKit.Base
 * cf. http://mochikit.com/doc/html/MochiKit/Base.html#fn-nodewalk
 */
function nodeWalkDepthFirst(node, visitor) {
    var children = visitor(node);
    if (children) {
	for(var i=0; i<children.length; i++) {
	    nodeWalkDepthFirst(children[i], visitor);
	}
    }
}


/* Returns true if all the given Array-like or string arguments are not empty (obj.length > 0) */
function isNotEmpty(obj) {
    for (var i = 0; i < arguments.length; i++) {
        var o = arguments[i];
        if (!(o && o.length)) {
            return false;
        }
    }
    return true;
}

/** this implementation comes from MochiKit  */
function formContents(elem/* = document.body */) {
    var names = [];
    var values = [];
    if (typeof(elem) == "undefined" || elem === null) {
        elem = document.body;
    } else {
        elem = getNode(elem);
    }
    nodeWalkDepthFirst(elem, function (elem) {
        var name = elem.name;
        if (isNotEmpty(name)) {
            var tagName = elem.tagName.toUpperCase();
            if (tagName === "INPUT"
                && (elem.type == "radio" || elem.type == "checkbox")
                && !elem.checked
               ) {
                return null;
            }
            if (tagName === "SELECT") {
                if (elem.type == "select-one") {
                    if (elem.selectedIndex >= 0) {
                        var opt = elem.options[elem.selectedIndex];
                        var v = opt.value;
                        if (!v) {
                            var h = opt.outerHTML;
                            // internet explorer sure does suck.
                            if (h && !h.match(/^[^>]+\svalue\s*=/i)) {
                                v = opt.text;
                            }
                        }
                        names.push(name);
                        values.push(v);
                        return null;
                    }
                    // no form elements?
                    names.push(name);
                    values.push("");
                    return null;
                } else {
                    var opts = elem.options;
                    if (!opts.length) {
                        names.push(name);
                        values.push("");
                        return null;
                    }
                    for (var i = 0; i < opts.length; i++) {
                        var opt = opts[i];
                        if (!opt.selected) {
                            continue;
                        }
                        var v = opt.value;
                        if (!v) {
                            var h = opt.outerHTML;
                            // internet explorer sure does suck.
                            if (h && !h.match(/^[^>]+\svalue\s*=/i)) {
                                v = opt.text;
                            }
                        }
                        names.push(name);
                        values.push(v);
                    }
                    return null;
                }
            }
            if (tagName === "FORM" || tagName === "P" || tagName === "SPAN"
                || tagName === "DIV"
               ) {
                return elem.childNodes;
            }
            names.push(name);
            values.push(elem.value || '');
            return null;
        }
        return elem.childNodes;
    });
    return [names, values];
}

function merge(array1, array2) {
    var result = [];
    for (var i=0,length=arguments.length; i<length; i++) {
	var array = arguments[i];
	for (var j=0,alength=array.length; j<alength; j++) {
	    result.push(array[j]);
	}
    }
    return result;
}

var KEYS = {
    KEY_ESC: 27,
    KEY_ENTER: 13
};