cubicweb/web/data/cubicweb.python.js
changeset 11057 0b59724cb3f2
parent 6921 c1fdf590712f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/web/data/cubicweb.python.js	Sat Jan 16 13:48:51 2016 +0100
@@ -0,0 +1,303 @@
+/**
+ * This file contains extensions for standard javascript types
+ *
+ */
+
+ONE_DAY = 86400000; // (in milliseconds)
+// ========== DATE EXTENSIONS ========== ///
+Date.prototype.equals = function(other) {
+    /* compare with other date ignoring time differences */
+    if (this.getYear() == other.getYear() && this.getMonth() == other.getMonth() && this.getDate() == other.getDate()) {
+        return true;
+    }
+    return false;
+};
+
+Date.prototype.add = function(days) {
+    var res = new Date();
+    res.setTime(this.getTime() + (days * ONE_DAY));
+    return res;
+};
+
+Date.prototype.sub = function(days) {
+    return this.add( - days);
+};
+
+Date.prototype.iadd = function(days) {
+    // in-place add
+    this.setTime(this.getTime() + (days * ONE_DAY));
+    // avoid strange rounding problems !!
+    this.setHours(12);
+};
+
+Date.prototype.isub = function(days) {
+    // in-place sub
+    this.setTime(this.getTime() - (days * ONE_DAY));
+};
+
+/**
+ * .. function:: Date.prototype.nextMonth()
+ *
+ * returns the first day of the next month
+ */
+Date.prototype.nextMonth = function() {
+    if (this.getMonth() == 11) {
+        var d = new Date(this.getFullYear() + 1, 0, 1);
+        return d;
+    } else {
+        var d2 = new Date(this.getFullYear(), this.getMonth() + 1, 1);
+        return d2;
+    }
+};
+
+/**
+ * .. function:: Date.prototype.getRealDay()
+ *
+ * returns the day of week, 0 being monday, 6 being sunday
+ */
+Date.prototype.getRealDay = function() {
+    // getDay() returns 0 for Sunday ==> 6 for Saturday
+    return (this.getDay() + 6) % 7;
+};
+
+Date.prototype.strftime = function(fmt) {
+    if (this.toLocaleFormat !== undefined) { // browser dependent
+        return this.toLocaleFormat(fmt);
+    }
+    // XXX implement at least a decent fallback implementation
+    return this.getFullYear() + '/' + (this.getMonth() + 1) + '/' + this.getDate();
+};
+
+var _DATE_FORMAT_REGXES = {
+    'Y': new RegExp('^-?[0-9]+'),
+    'd': new RegExp('^[0-9]{1,2}'),
+    'm': new RegExp('^[0-9]{1,2}'),
+    'H': new RegExp('^[0-9]{1,2}'),
+    'M': new RegExp('^[0-9]{1,2}')
+};
+
+/**
+ * .. function:: _parseDate(datestring, format)
+ *
+ * _parseData does the actual parsing job needed by `strptime`
+ */
+function _parseDate(datestring, format) {
+    var skip0 = new RegExp('^0*[0-9]+');
+    var parsed = {};
+    for (var i1 = 0, i2 = 0; i1 < format.length; i1++, i2++) {
+        var c1 = format.charAt(i1);
+        var c2 = datestring.charAt(i2);
+        if (c1 == '%') {
+            c1 = format.charAt(++i1);
+            var data = _DATE_FORMAT_REGXES[c1].exec(datestring.substring(i2));
+            if (!data.length) {
+                return null;
+            }
+            data = data[0];
+            i2 += data.length - 1;
+            var value = parseInt(data, 10);
+            if (isNaN(value)) {
+                return null;
+            }
+            parsed[c1] = value;
+            continue;
+        }
+        if (c1 != c2) {
+            return null;
+        }
+    }
+    return parsed;
+}
+
+/**
+ * .. function:: strptime(datestring, format)
+ *
+ * basic implementation of strptime. The only recognized formats
+ * defined in _DATE_FORMAT_REGEXES (i.e. %Y, %d, %m, %H, %M)
+ */
+function strptime(datestring, format) {
+    var parsed = _parseDate(datestring, format);
+    if (!parsed) {
+        return null;
+    }
+    // create initial date (!!! year=0 means 1900 !!!)
+    var date = new Date(0, 0, 1, 0, 0);
+    date.setFullYear(0); // reset to year 0
+    if (parsed.Y) {
+        date.setFullYear(parsed.Y);
+    }
+    if (parsed.m) {
+        if (parsed.m < 1 || parsed.m > 12) {
+            return null;
+        }
+        // !!! month indexes start at 0 in javascript !!!
+        date.setMonth(parsed.m - 1);
+    }
+    if (parsed.d) {
+        if (parsed.m < 1 || parsed.m > 31) {
+            return null;
+        }
+        date.setDate(parsed.d);
+    }
+    if (parsed.H) {
+        if (parsed.H < 0 || parsed.H > 23) {
+            return null;
+        }
+        date.setHours(parsed.H);
+    }
+    if (parsed.M) {
+        if (parsed.M < 0 || parsed.M > 59) {
+            return null;
+        }
+        date.setMinutes(parsed.M);
+    }
+    return date;
+}
+
+// ========== END OF DATE EXTENSIONS ========== ///
+// ========== STRING EXTENSIONS ========== //
+/**
+ * .. function:: String.prototype.startswith(prefix)
+ *
+ * python-like startsWith method for js strings
+ * >>>
+ */
+String.prototype.startswith = function(prefix) {
+    return this.indexOf(prefix) == 0;
+};
+
+/**
+ * .. function:: String.prototype.endswith(suffix)
+ *
+ * python-like endsWith method for js strings
+ */
+String.prototype.endswith = function(suffix) {
+    var startPos = this.length - suffix.length;
+    if (startPos < 0) {
+        return false;
+    }
+    return this.lastIndexOf(suffix, startPos) == startPos;
+};
+
+/**
+ * .. function:: String.prototype.strip()
+ *
+ * python-like strip method for js strings
+ */
+String.prototype.strip = function() {
+    return this.replace(/^\s*(.*?)\s*$/, "$1");
+};
+
+/**
+ * .. function:: String.prototype.rstrip()
+ *
+ * python-like rstrip method for js strings
+ */
+String.prototype.rstrip = function(str) {
+    if (!str) { str = '\s' ; }
+    return this.replace(new RegExp('^(.*?)' + str + '*$'), "$1");
+};
+
+// ========= class factories ========= //
+
+/**
+ * .. function:: makeUnboundMethod(meth)
+ *
+ * transforms a function into an unbound method
+ */
+function makeUnboundMethod(meth) {
+    function unboundMeth(self) {
+        var newargs = cw.utils.sliceList(arguments, 1);
+        return meth.apply(self, newargs);
+    }
+    unboundMeth.__name__ = meth.__name__;
+    return unboundMeth;
+}
+
+function attachMethodToClass(cls, methname, meth) {
+    meth.__name__ = methname;
+    // XXX : this is probably bad for memory usage
+    cls.__dict__[methname] = meth;
+    cls[methname] = makeUnboundMethod(meth); // for the class itself
+    cls.prototype[methname] = meth; // for the instance
+}
+
+/**
+ * .. function:: _isAttrSkipped(attrname)
+ *
+ * simple internal function that tells if the attribute should
+ * be copied from baseclasses or not
+ */
+function _isAttrSkipped(attrname) {
+    var skipped = ['__class__', '__dict__', '__bases__', 'prototype'];
+    for (var i = 0; i < skipped.length; i++) {
+        if (skipped[i] == attrname) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * .. function:: makeConstructor(userctor)
+ *
+ * internal function used to build the class constructor
+ */
+function makeConstructor(userctor) {
+    return function() {
+        // this is a proxy to user's __init__
+        if (userctor) {
+            userctor.apply(this, arguments);
+        }
+    };
+}
+
+/**
+ * .. function:: defclass(name, bases, classdict)
+ *
+ * this is a js class factory. objects returned by this function behave
+ * more or less like a python class. The `class` function prototype is
+ * inspired by the python `type` builtin
+ *
+ * .. Note::
+ *
+ *    * methods are _STATICALLY_ attached when the class it created
+ *    * multiple inheritance was never tested, which means it doesn't work ;-)
+ */
+function defclass(name, bases, classdict) {
+    var baseclasses = bases || [];
+
+    // this is the static inheritance approach (<=> differs from python)
+    var basemeths = {};
+    var reverseLookup = [];
+    for (var i = baseclasses.length - 1; i >= 0; i--) {
+        reverseLookup.push(baseclasses[i]);
+    }
+    reverseLookup.push({
+        '__dict__': classdict
+    });
+
+    for (var i = 0; i < reverseLookup.length; i++) {
+        var cls = reverseLookup[i];
+        for (prop in cls.__dict__) {
+            // XXX hack to avoid __init__, __bases__...
+            if (!_isAttrSkipped(prop)) {
+                basemeths[prop] = cls.__dict__[prop];
+            }
+        }
+    }
+    var userctor = basemeths['__init__'];
+    var constructor = makeConstructor(userctor);
+
+    // python-like interface
+    constructor.__name__ = name;
+    constructor.__bases__ = baseclasses;
+    constructor.__dict__ = {};
+    constructor.prototype.__class__ = constructor;
+    // make bound / unbound methods
+    for (methname in basemeths) {
+        attachMethodToClass(constructor, methname, basemeths[methname]);
+    }
+
+    return constructor;
+}