web/data/cubicweb.python.js
changeset 0 b97547f5f1fa
child 156 261bf6d647eb
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 /*
       
     2  * This file contains extensions for standard javascript types
       
     3  *
       
     4  */
       
     5 
       
     6 ONE_DAY = 86400000; // (in milliseconds)
       
     7 
       
     8 // ========== DATE EXTENSIONS ========== ///
       
     9 
       
    10 Date.prototype.equals = function(other) {
       
    11     /* compare with other date ignoring time differences */
       
    12     if (this.getYear() == other.getYear() &&
       
    13 	this.getMonth() == other.getMonth() &&
       
    14 	this.getDate() == other.getDate()) {
       
    15 	return true;
       
    16     }
       
    17     return false;
       
    18 }
       
    19 
       
    20 Date.prototype.add = function(days) {
       
    21     var res = new Date()
       
    22     res.setTime(this.getTime() + (days * ONE_DAY))
       
    23     return res
       
    24 }
       
    25 
       
    26 Date.prototype.sub = function(days) {
       
    27     return this.add(-days);
       
    28 }
       
    29 
       
    30 Date.prototype.iadd = function(days) {
       
    31     // in-place add
       
    32     this.setTime(this.getTime() + (days * ONE_DAY))
       
    33     // avoid strange rounding problems !!
       
    34     this.setHours(12);
       
    35 }
       
    36 
       
    37 Date.prototype.isub = function(days) {
       
    38     // in-place sub
       
    39     this.setTime(this.getTime() - (days * ONE_DAY))
       
    40 }
       
    41 
       
    42 /*
       
    43  * returns the first day of the next month
       
    44  */
       
    45 Date.prototype.nextMonth = function() {
       
    46     if (this.getMonth() == 11) {
       
    47 	var d =new Date(this.getFullYear()+1, 0, 1);
       
    48 	return d;
       
    49     } else {
       
    50 	var d2 = new Date(this.getFullYear(), this.getMonth()+1, 1);
       
    51 	return d2;
       
    52     }
       
    53 }
       
    54 
       
    55 /*
       
    56  * returns the day of week, 0 being monday, 6 being sunday
       
    57  */
       
    58 Date.prototype.getRealDay = function() {
       
    59     // getDay() returns 0 for Sunday ==> 6 for Saturday
       
    60     return (this.getDay()+6) % 7;
       
    61 }
       
    62 
       
    63 
       
    64 var _DATE_FORMAT_REGXES = {
       
    65     'Y': new RegExp('^-?[0-9]+'),
       
    66     'd': new RegExp('^[0-9]{1,2}'),
       
    67     'm': new RegExp('^[0-9]{1,2}'),
       
    68     'H': new RegExp('^[0-9]{1,2}'),
       
    69     'M': new RegExp('^[0-9]{1,2}')
       
    70 }
       
    71 
       
    72 /*
       
    73  * _parseData does the actual parsing job needed by `strptime`
       
    74  */
       
    75 function _parseDate(datestring, format) {
       
    76     var skip0 = new RegExp('^0*[0-9]+');
       
    77     var parsed = {};
       
    78     for (var i1=0,i2=0;i1<format.length;i1++,i2++) {
       
    79 	var c1 = format[i1];
       
    80 	var c2 = datestring[i2];
       
    81 	if (c1 == '%') {
       
    82 	    c1 = format[++i1];
       
    83 	    var data = _DATE_FORMAT_REGXES[c1].exec(datestring.substring(i2));
       
    84 	    if (!data.length) {
       
    85 		return null;
       
    86 	    }
       
    87 	    data = data[0];
       
    88 	    i2 += data.length-1;
       
    89 	    var value = parseInt(data, 10);
       
    90 	    if (isNaN(value)) {
       
    91 		return null;
       
    92 	    }
       
    93 	    parsed[c1] = value;
       
    94 	    continue;
       
    95 	}
       
    96 	if (c1 != c2) {
       
    97 	    return null;
       
    98 	}
       
    99     }
       
   100     return parsed;
       
   101 }
       
   102 
       
   103 /*
       
   104  * basic implementation of strptime. The only recognized formats
       
   105  * defined in _DATE_FORMAT_REGEXES (i.e. %Y, %d, %m, %H, %M)
       
   106  */
       
   107 function strptime(datestring, format) {
       
   108     var parsed = _parseDate(datestring, format);
       
   109     if (!parsed) {
       
   110 	return null;
       
   111     }
       
   112     // create initial date (!!! year=0 means 1900 !!!)
       
   113     date = new Date(0, 0, 1, 0, 0);
       
   114     date.setFullYear(0); // reset to year 0
       
   115     if (parsed.Y) {
       
   116 	date.setFullYear(parsed.Y);
       
   117     }
       
   118     if (parsed.m) {
       
   119 	if (parsed.m < 1 || parsed.m > 12) {
       
   120 	    return null;
       
   121 	}
       
   122 	// !!! month indexes start at 0 in javascript !!!
       
   123 	date.setMonth(parsed.m - 1);
       
   124     }
       
   125     if (parsed.d) {
       
   126 	if (parsed.m < 1 || parsed.m > 31) {
       
   127 	    return null;
       
   128 	}
       
   129 	date.setDate(parsed.d)
       
   130     }
       
   131     if (parsed.H) {
       
   132 	if (parsed.H < 0 || parsed.H > 23) {
       
   133 	    return null;
       
   134 	}
       
   135 	date.setHours(parsed.H)
       
   136     }
       
   137     if (parsed.M) {
       
   138 	if (parsed.M < 0 || parsed.M > 59) {
       
   139 	    return null;
       
   140 	}
       
   141 	date.setMinutes(parsed.M)
       
   142     }
       
   143     return date;
       
   144 }
       
   145 
       
   146 // ========== END OF DATE EXTENSIONS ========== ///
       
   147 
       
   148 
       
   149 
       
   150 // ========== ARRAY EXTENSIONS ========== ///
       
   151 Array.prototype.contains = function(element) {
       
   152     return findValue(this, element) != -1;
       
   153 }
       
   154 
       
   155 // ========== END OF ARRAY EXTENSIONS ========== ///
       
   156 
       
   157 
       
   158 
       
   159 // ========== STRING EXTENSIONS ========== //
       
   160 
       
   161 /* python-like startsWith method for js strings
       
   162  * >>>
       
   163  */
       
   164 String.prototype.startsWith = function(prefix) {
       
   165     return this.indexOf(prefix) == 0;
       
   166 }
       
   167 
       
   168 /* python-like endsWith method for js strings */
       
   169 String.prototype.endsWith = function(suffix) {
       
   170     var startPos = this.length - suffix.length;
       
   171     if (startPos < 0) { return false; }
       
   172     return this.lastIndexOf(suffix, startPos) == startPos;
       
   173 }
       
   174 
       
   175 /* python-like strip method for js strings */
       
   176 String.prototype.strip = function() {
       
   177     return this.replace(/^\s*(.*?)\s*$/, "$1");
       
   178 };
       
   179 
       
   180 /* py-equiv: string in list */
       
   181 String.prototype.in_ = function(values) {
       
   182     return findValue(values, this) != -1;
       
   183 }
       
   184 
       
   185 /* py-equiv: str.join(list) */
       
   186 String.prototype.join = function(args) {
       
   187     return args.join(this);
       
   188 }
       
   189 
       
   190 /* python-like list builtin
       
   191  * transforms an iterable in a js sequence
       
   192  * >>> gen = ifilter(function(x) {return x%2==0}, range(10))
       
   193  * >>> s = list(gen)
       
   194  * [0,2,4,6,8]
       
   195  */
       
   196 function list(iterable) {
       
   197     iterator = iter(iterable);
       
   198     var result = [];
       
   199     while (true) {
       
   200 	/* iterates until StopIteration occurs */
       
   201 	try {
       
   202 	    result.push(iterator.next());
       
   203 	} catch (exc) {
       
   204 	    if (exc != StopIteration) { throw exc; }
       
   205 	    return result;
       
   206 	}
       
   207     }
       
   208 }
       
   209 
       
   210 /* py-equiv: getattr(obj, attrname, default=None) */
       
   211 function getattr(obj, attrname, defaultValue) {
       
   212     // when not passed, defaultValue === undefined
       
   213     return obj[attrname] || defaultValue;
       
   214 }
       
   215 
       
   216 /* py-equiv: operator.attrgetter */
       
   217 function attrgetter(attrname) {
       
   218     return function(obj) { return getattr(obj, attrname); };
       
   219 }
       
   220 
       
   221 
       
   222 /* returns a subslice of `lst` using `start`/`stop`/`step`
       
   223  * start, stop might be negative
       
   224  *
       
   225  * >>> sliceList(['a', 'b', 'c', 'd', 'e', 'f'], 2)
       
   226  * ['c', 'd', 'e', 'f']
       
   227  * >>> sliceList(['a', 'b', 'c', 'd', 'e', 'f'], 2, -2)
       
   228  * ['c', 'd']
       
   229  * >>> sliceList(['a', 'b', 'c', 'd', 'e', 'f'], -3)
       
   230  * ['d', 'e', 'f']
       
   231  */
       
   232 function sliceList(lst, start, stop, step) {
       
   233     var start = start || 0;
       
   234     var stop = stop || lst.length;
       
   235     var step = step || 1;
       
   236     if (stop < 0) {
       
   237 	stop = max(lst.length+stop, 0);
       
   238     }
       
   239     if (start < 0) {
       
   240 	start = min(lst.length+start, lst.length);
       
   241     }
       
   242     var result = [];
       
   243     for (var i=start; i < stop; i+=step) {
       
   244 	result.push(lst[i]);
       
   245     }
       
   246     return result;
       
   247 }
       
   248 
       
   249 /* returns a partial func that calls a mehod on its argument
       
   250  * py-equiv: return lambda obj: getattr(obj, methname)(*args)
       
   251  */
       
   252 function methodcaller(methname) {
       
   253     var args = sliceList(arguments, 1);
       
   254     return function(obj) {
       
   255 	return obj[methname].apply(obj, args);
       
   256     };
       
   257 }
       
   258 
       
   259 /* use MochiKit's listMin / listMax */
       
   260 function min() { return listMin(arguments); }
       
   261 function max() { return listMax(arguments); }
       
   262 
       
   263 // tricky multiple assign
       
   264 // function assign(lst, varnames) {
       
   265 //     var length = min(lst.length, varnames.length);
       
   266 //     for(var i=0; i<length; i++) {
       
   267 // 	window[varnames[i]] = lst[i];
       
   268 //     }
       
   269 // }
       
   270 
       
   271 /*
       
   272  * >>> d = dict(["x", "y", "z"], [0, 1, 2])
       
   273  * >>> d['y']
       
   274  * 1
       
   275  * >>> d.y
       
   276  * 1
       
   277  */
       
   278 function dict(keys, values) {
       
   279     if (keys.length != values.length) {
       
   280 	throw "got different number of keys and values !";
       
   281     }
       
   282     var newobj = {};
       
   283     for(var i=0; i<keys.length; i++) {
       
   284 	newobj[keys[i]] = values[i];
       
   285     }
       
   286     return newobj;
       
   287 }
       
   288 
       
   289 
       
   290 function concat() {
       
   291     return ''.join(list(arguments));
       
   292 }
       
   293 
       
   294 
       
   295 /**** class factories ****/
       
   296 
       
   297 // transforms a function into an unbound method
       
   298 function makeUnboundMethod(meth) {
       
   299     function unboundMeth(self) {
       
   300 	var newargs = sliceList(arguments, 1);
       
   301 	return meth.apply(self, newargs);
       
   302     }
       
   303     unboundMeth.__name__ = meth.__name__;
       
   304     return unboundMeth;
       
   305 }
       
   306 
       
   307 function attachMethodToClass(cls, methname, meth) {
       
   308     meth.__name__ = methname;
       
   309     // XXX : this is probably bad for memory usage
       
   310     cls.__dict__[methname] = meth;
       
   311     cls[methname] = makeUnboundMethod(meth); // for the class itself
       
   312     cls.prototype[methname] = meth; // for the instance
       
   313 }
       
   314 
       
   315 // simple internal function that tells if the attribute should
       
   316 // be copied from baseclasses or not
       
   317 function _isAttrSkipped(attrname) {
       
   318     var skipped = ['__class__', '__dict__', '__bases__', 'prototype'];
       
   319     for (var i=0; i < skipped.length; i++) {
       
   320 	if (skipped[i] == attrname) {
       
   321 	    return true;
       
   322 	}
       
   323     }
       
   324     return false;
       
   325 }
       
   326 
       
   327 // internal function used to build the class constructor
       
   328 function makeConstructor(userctor) {
       
   329     return function() {
       
   330 	// this is a proxy to user's __init__
       
   331 	if(userctor) {
       
   332 	    userctor.apply(this, arguments);
       
   333 	}
       
   334     };
       
   335 }
       
   336 
       
   337 /* this is a js class factory. objects returned by this function behave
       
   338  * more or less like a python class. The `class` function prototype is
       
   339  * inspired by the python `type` builtin
       
   340  * Important notes :
       
   341  *  -> methods are _STATICALLY_ attached when the class it created
       
   342  *  -> multiple inheritance was never tested, which means it doesn't work ;-)
       
   343  */
       
   344 function defclass(name, bases, classdict) {
       
   345     var baseclasses = bases || [];
       
   346 
       
   347     // this is the static inheritance approach (<=> differs from python)
       
   348     var basemeths = {};
       
   349     var reverseLookup = [];
       
   350     for(var i=baseclasses.length-1; i >= 0; i--) {
       
   351 	reverseLookup.push(baseclasses[i]);
       
   352     }
       
   353     reverseLookup.push({'__dict__' : classdict});
       
   354 
       
   355     for(var i=0; i < reverseLookup.length; i++) {
       
   356 	var cls = reverseLookup[i];
       
   357 	for (prop in cls.__dict__) {
       
   358 	    // XXX hack to avoid __init__, __bases__...
       
   359 	    if ( !_isAttrSkipped(prop) ) {
       
   360 		basemeths[prop] = cls.__dict__[prop];
       
   361 	    }
       
   362 	}
       
   363     }
       
   364     var userctor = basemeths['__init__'];
       
   365     constructor = makeConstructor(userctor);
       
   366 
       
   367     // python-like interface
       
   368     constructor.__name__ = name;
       
   369     constructor.__bases__ = baseclasses;
       
   370     constructor.__dict__ = {};
       
   371     constructor.prototype.__class__ = constructor;
       
   372     // make bound / unbound methods
       
   373     for (methname in basemeths) {
       
   374 	attachMethodToClass(constructor, methname, basemeths[methname]);
       
   375     }
       
   376 
       
   377     return constructor;
       
   378 }
       
   379 
       
   380 // Not really python-like
       
   381 CubicWeb = {};
       
   382 // XXX backward compatibility
       
   383 Erudi = CubicWeb;
       
   384 CubicWeb.loaded = [];
       
   385 CubicWeb.require = function(module) {
       
   386     if (!CubicWeb.loaded.contains(module)) {
       
   387 	// a CubicWeb.load_javascript(module) function would require a dependency on ajax.js
       
   388 	log(module, ' is required but not loaded');
       
   389     }
       
   390 };
       
   391 
       
   392 CubicWeb.provide = function(module) {
       
   393     if (!CubicWeb.loaded.contains(module)) {
       
   394 	CubicWeb.loaded.push(module);
       
   395     }
       
   396 };
       
   397 
       
   398 CubicWeb.provide('python.js');