cubicweb/web/data/cubicweb.python.js
changeset 11057 0b59724cb3f2
parent 6921 c1fdf590712f
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 /**
       
     2  * This file contains extensions for standard javascript types
       
     3  *
       
     4  */
       
     5 
       
     6 ONE_DAY = 86400000; // (in milliseconds)
       
     7 // ========== DATE EXTENSIONS ========== ///
       
     8 Date.prototype.equals = function(other) {
       
     9     /* compare with other date ignoring time differences */
       
    10     if (this.getYear() == other.getYear() && this.getMonth() == other.getMonth() && this.getDate() == other.getDate()) {
       
    11         return true;
       
    12     }
       
    13     return false;
       
    14 };
       
    15 
       
    16 Date.prototype.add = function(days) {
       
    17     var res = new Date();
       
    18     res.setTime(this.getTime() + (days * ONE_DAY));
       
    19     return res;
       
    20 };
       
    21 
       
    22 Date.prototype.sub = function(days) {
       
    23     return this.add( - days);
       
    24 };
       
    25 
       
    26 Date.prototype.iadd = function(days) {
       
    27     // in-place add
       
    28     this.setTime(this.getTime() + (days * ONE_DAY));
       
    29     // avoid strange rounding problems !!
       
    30     this.setHours(12);
       
    31 };
       
    32 
       
    33 Date.prototype.isub = function(days) {
       
    34     // in-place sub
       
    35     this.setTime(this.getTime() - (days * ONE_DAY));
       
    36 };
       
    37 
       
    38 /**
       
    39  * .. function:: Date.prototype.nextMonth()
       
    40  *
       
    41  * returns the first day of the next month
       
    42  */
       
    43 Date.prototype.nextMonth = function() {
       
    44     if (this.getMonth() == 11) {
       
    45         var d = new Date(this.getFullYear() + 1, 0, 1);
       
    46         return d;
       
    47     } else {
       
    48         var d2 = new Date(this.getFullYear(), this.getMonth() + 1, 1);
       
    49         return d2;
       
    50     }
       
    51 };
       
    52 
       
    53 /**
       
    54  * .. function:: Date.prototype.getRealDay()
       
    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 Date.prototype.strftime = function(fmt) {
       
    64     if (this.toLocaleFormat !== undefined) { // browser dependent
       
    65         return this.toLocaleFormat(fmt);
       
    66     }
       
    67     // XXX implement at least a decent fallback implementation
       
    68     return this.getFullYear() + '/' + (this.getMonth() + 1) + '/' + this.getDate();
       
    69 };
       
    70 
       
    71 var _DATE_FORMAT_REGXES = {
       
    72     'Y': new RegExp('^-?[0-9]+'),
       
    73     'd': new RegExp('^[0-9]{1,2}'),
       
    74     'm': new RegExp('^[0-9]{1,2}'),
       
    75     'H': new RegExp('^[0-9]{1,2}'),
       
    76     'M': new RegExp('^[0-9]{1,2}')
       
    77 };
       
    78 
       
    79 /**
       
    80  * .. function:: _parseDate(datestring, format)
       
    81  *
       
    82  * _parseData does the actual parsing job needed by `strptime`
       
    83  */
       
    84 function _parseDate(datestring, format) {
       
    85     var skip0 = new RegExp('^0*[0-9]+');
       
    86     var parsed = {};
       
    87     for (var i1 = 0, i2 = 0; i1 < format.length; i1++, i2++) {
       
    88         var c1 = format.charAt(i1);
       
    89         var c2 = datestring.charAt(i2);
       
    90         if (c1 == '%') {
       
    91             c1 = format.charAt(++i1);
       
    92             var data = _DATE_FORMAT_REGXES[c1].exec(datestring.substring(i2));
       
    93             if (!data.length) {
       
    94                 return null;
       
    95             }
       
    96             data = data[0];
       
    97             i2 += data.length - 1;
       
    98             var value = parseInt(data, 10);
       
    99             if (isNaN(value)) {
       
   100                 return null;
       
   101             }
       
   102             parsed[c1] = value;
       
   103             continue;
       
   104         }
       
   105         if (c1 != c2) {
       
   106             return null;
       
   107         }
       
   108     }
       
   109     return parsed;
       
   110 }
       
   111 
       
   112 /**
       
   113  * .. function:: strptime(datestring, format)
       
   114  *
       
   115  * basic implementation of strptime. The only recognized formats
       
   116  * defined in _DATE_FORMAT_REGEXES (i.e. %Y, %d, %m, %H, %M)
       
   117  */
       
   118 function strptime(datestring, format) {
       
   119     var parsed = _parseDate(datestring, format);
       
   120     if (!parsed) {
       
   121         return null;
       
   122     }
       
   123     // create initial date (!!! year=0 means 1900 !!!)
       
   124     var date = new Date(0, 0, 1, 0, 0);
       
   125     date.setFullYear(0); // reset to year 0
       
   126     if (parsed.Y) {
       
   127         date.setFullYear(parsed.Y);
       
   128     }
       
   129     if (parsed.m) {
       
   130         if (parsed.m < 1 || parsed.m > 12) {
       
   131             return null;
       
   132         }
       
   133         // !!! month indexes start at 0 in javascript !!!
       
   134         date.setMonth(parsed.m - 1);
       
   135     }
       
   136     if (parsed.d) {
       
   137         if (parsed.m < 1 || parsed.m > 31) {
       
   138             return null;
       
   139         }
       
   140         date.setDate(parsed.d);
       
   141     }
       
   142     if (parsed.H) {
       
   143         if (parsed.H < 0 || parsed.H > 23) {
       
   144             return null;
       
   145         }
       
   146         date.setHours(parsed.H);
       
   147     }
       
   148     if (parsed.M) {
       
   149         if (parsed.M < 0 || parsed.M > 59) {
       
   150             return null;
       
   151         }
       
   152         date.setMinutes(parsed.M);
       
   153     }
       
   154     return date;
       
   155 }
       
   156 
       
   157 // ========== END OF DATE EXTENSIONS ========== ///
       
   158 // ========== STRING EXTENSIONS ========== //
       
   159 /**
       
   160  * .. function:: String.prototype.startswith(prefix)
       
   161  *
       
   162  * python-like startsWith method for js strings
       
   163  * >>>
       
   164  */
       
   165 String.prototype.startswith = function(prefix) {
       
   166     return this.indexOf(prefix) == 0;
       
   167 };
       
   168 
       
   169 /**
       
   170  * .. function:: String.prototype.endswith(suffix)
       
   171  *
       
   172  * python-like endsWith method for js strings
       
   173  */
       
   174 String.prototype.endswith = function(suffix) {
       
   175     var startPos = this.length - suffix.length;
       
   176     if (startPos < 0) {
       
   177         return false;
       
   178     }
       
   179     return this.lastIndexOf(suffix, startPos) == startPos;
       
   180 };
       
   181 
       
   182 /**
       
   183  * .. function:: String.prototype.strip()
       
   184  *
       
   185  * python-like strip method for js strings
       
   186  */
       
   187 String.prototype.strip = function() {
       
   188     return this.replace(/^\s*(.*?)\s*$/, "$1");
       
   189 };
       
   190 
       
   191 /**
       
   192  * .. function:: String.prototype.rstrip()
       
   193  *
       
   194  * python-like rstrip method for js strings
       
   195  */
       
   196 String.prototype.rstrip = function(str) {
       
   197     if (!str) { str = '\s' ; }
       
   198     return this.replace(new RegExp('^(.*?)' + str + '*$'), "$1");
       
   199 };
       
   200 
       
   201 // ========= class factories ========= //
       
   202 
       
   203 /**
       
   204  * .. function:: makeUnboundMethod(meth)
       
   205  *
       
   206  * transforms a function into an unbound method
       
   207  */
       
   208 function makeUnboundMethod(meth) {
       
   209     function unboundMeth(self) {
       
   210         var newargs = cw.utils.sliceList(arguments, 1);
       
   211         return meth.apply(self, newargs);
       
   212     }
       
   213     unboundMeth.__name__ = meth.__name__;
       
   214     return unboundMeth;
       
   215 }
       
   216 
       
   217 function attachMethodToClass(cls, methname, meth) {
       
   218     meth.__name__ = methname;
       
   219     // XXX : this is probably bad for memory usage
       
   220     cls.__dict__[methname] = meth;
       
   221     cls[methname] = makeUnboundMethod(meth); // for the class itself
       
   222     cls.prototype[methname] = meth; // for the instance
       
   223 }
       
   224 
       
   225 /**
       
   226  * .. function:: _isAttrSkipped(attrname)
       
   227  *
       
   228  * simple internal function that tells if the attribute should
       
   229  * be copied from baseclasses or not
       
   230  */
       
   231 function _isAttrSkipped(attrname) {
       
   232     var skipped = ['__class__', '__dict__', '__bases__', 'prototype'];
       
   233     for (var i = 0; i < skipped.length; i++) {
       
   234         if (skipped[i] == attrname) {
       
   235             return true;
       
   236         }
       
   237     }
       
   238     return false;
       
   239 }
       
   240 
       
   241 /**
       
   242  * .. function:: makeConstructor(userctor)
       
   243  *
       
   244  * internal function used to build the class constructor
       
   245  */
       
   246 function makeConstructor(userctor) {
       
   247     return function() {
       
   248         // this is a proxy to user's __init__
       
   249         if (userctor) {
       
   250             userctor.apply(this, arguments);
       
   251         }
       
   252     };
       
   253 }
       
   254 
       
   255 /**
       
   256  * .. function:: defclass(name, bases, classdict)
       
   257  *
       
   258  * this is a js class factory. objects returned by this function behave
       
   259  * more or less like a python class. The `class` function prototype is
       
   260  * inspired by the python `type` builtin
       
   261  *
       
   262  * .. Note::
       
   263  *
       
   264  *    * methods are _STATICALLY_ attached when the class it created
       
   265  *    * multiple inheritance was never tested, which means it doesn't work ;-)
       
   266  */
       
   267 function defclass(name, bases, classdict) {
       
   268     var baseclasses = bases || [];
       
   269 
       
   270     // this is the static inheritance approach (<=> differs from python)
       
   271     var basemeths = {};
       
   272     var reverseLookup = [];
       
   273     for (var i = baseclasses.length - 1; i >= 0; i--) {
       
   274         reverseLookup.push(baseclasses[i]);
       
   275     }
       
   276     reverseLookup.push({
       
   277         '__dict__': classdict
       
   278     });
       
   279 
       
   280     for (var i = 0; i < reverseLookup.length; i++) {
       
   281         var cls = reverseLookup[i];
       
   282         for (prop in cls.__dict__) {
       
   283             // XXX hack to avoid __init__, __bases__...
       
   284             if (!_isAttrSkipped(prop)) {
       
   285                 basemeths[prop] = cls.__dict__[prop];
       
   286             }
       
   287         }
       
   288     }
       
   289     var userctor = basemeths['__init__'];
       
   290     var constructor = makeConstructor(userctor);
       
   291 
       
   292     // python-like interface
       
   293     constructor.__name__ = name;
       
   294     constructor.__bases__ = baseclasses;
       
   295     constructor.__dict__ = {};
       
   296     constructor.prototype.__class__ = constructor;
       
   297     // make bound / unbound methods
       
   298     for (methname in basemeths) {
       
   299         attachMethodToClass(constructor, methname, basemeths[methname]);
       
   300     }
       
   301 
       
   302     return constructor;
       
   303 }