/**

* Utilities
*/

createClass = function (/* [baseclass, [mixin, …]], definition */) {

var Class, args;
Class = function () {
    this.init.apply(this, arguments);
};
if (arguments.length > 1) {
    if (arguments[0]) {
        Class.prototype = $.extend(new arguments[0](), arguments[arguments.length - 1]);
        Class._super = arguments[0].prototype;
    } else {
        Class.prototype = arguments[arguments.length - 1];
    }
    if (arguments.length > 2) {
        args = Array.prototype.slice.call(arguments, 1, -1);
        args.unshift(Class.prototype);
        $.extend.apply($, args);
    }
} else {
    Class.prototype = arguments[0];
}
Class.prototype.cls = Class;
return Class;

};

/**

* Wraps a format string for tooltips
* {{x}}
* {{x.2}
* {{x:months}}
*/

$.SPFormatClass = SPFormat = createClass({

fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g,
precre: /(\w+)\.(\d+)/,

init: function (format, fclass) {
    this.format = format;
    this.fclass = fclass;
},

render: function (fieldset, lookups, options) {
    var self = this,
        fields = fieldset,
        match, token, lookupkey, fieldvalue, prec;
    return this.format.replace(this.fre, function () {
        var lookup;
        token = arguments[1];
        lookupkey = arguments[3];
        match = self.precre.exec(token);
        if (match) {
            prec = match[2];
            token = match[1];
        } else {
            prec = false;
        }
        fieldvalue = fields[token];
        if (fieldvalue === undefined) {
            return '';
        }
        if (lookupkey && lookups && lookups[lookupkey]) {
            lookup = lookups[lookupkey];
            if (lookup.get) { // RangeMap
                return lookups[lookupkey].get(fieldvalue) || fieldvalue;
            } else {
                return lookups[lookupkey][fieldvalue] || fieldvalue;
            }
        }
        if (isNumber(fieldvalue)) {
            if (options.get('numberFormatter')) {
                fieldvalue = options.get('numberFormatter')(fieldvalue);
            } else {
                fieldvalue = formatNumber(fieldvalue, prec,
                    options.get('numberDigitGroupCount'),
                    options.get('numberDigitGroupSep'),
                    options.get('numberDecimalMark'));
            }
        }
        return fieldvalue;
    });
}

});

// convience method to avoid needing the new operator $.spformat = function(format, fclass) {

return new SPFormat(format, fclass);

};

clipval = function (val, min, max) {

if (val < min) {
    return min;
}
if (val > max) {
    return max;
}
return val;

};

quartile = function (values, q) {

var vl;
if (q === 2) {
    vl = Math.floor(values.length / 2);
    return values.length % 2 ? values[vl] : (values[vl-1] + values[vl]) / 2;
} else {
    if (values.length % 2 ) { // odd
        vl = (values.length * q + q) / 4;
        return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];
    } else { //even
        vl = (values.length * q + 2) / 4;
        return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 :  values[vl-1];

    }
}

};

normalizeValue = function (val) {

var nf;
switch (val) {
    case 'undefined':
        val = undefined;
        break;
    case 'null':
        val = null;
        break;
    case 'true':
        val = true;
        break;
    case 'false':
        val = false;
        break;
    default:
        nf = parseFloat(val);
        if (val == nf) {
            val = nf;
        }
}
return val;

};

normalizeValues = function (vals) {

var i, result = [];
for (i = vals.length; i--;) {
    result[i] = normalizeValue(vals[i]);
}
return result;

};

remove = function (vals, filter) {

var i, vl, result = [];
for (i = 0, vl = vals.length; i < vl; i++) {
    if (vals[i] !== filter) {
        result.push(vals[i]);
    }
}
return result;

};

isNumber = function (num) {

return !isNaN(parseFloat(num)) && isFinite(num);

};

formatNumber = function (num, prec, groupsize, groupsep, decsep) {

var p, i;
num = (prec === false ? parseFloat(num).toString() : num.toFixed(prec)).split('');
p = (p = $.inArray('.', num)) < 0 ? num.length : p;
if (p < num.length) {
    num[p] = decsep;
}
for (i = p - groupsize; i > 0; i -= groupsize) {
    num.splice(i, 0, groupsep);
}
return num.join('');

};

// determine if all values of an array match a value // returns true if the array is empty all = function (val, arr, ignoreNull) {

var i;
for (i = arr.length; i--; ) {
    if (ignoreNull && arr[i] === null) continue;
    if (arr[i] !== val) {
        return false;
    }
}
return true;

};

// sums the numeric values in an array, ignoring other values sum = function (vals) {

var total = 0, i;
for (i = vals.length; i--;) {
    total += typeof vals[i] === 'number' ? vals[i] : 0;
}
return total;

};

ensureArray = function (val) {

return $.isArray(val) ? val : [val];

};

// paulirish.com/2008/bookmarklet-inject-new-css-rules/ addCSS = function(css) {

var tag, iefail;
if (document.createStyleSheet) {
    try {
        document.createStyleSheet().cssText = css;
        return;
    } catch (e) {
        // IE <= 9 maxes out at 31 stylesheets; inject into page instead.
        iefail = true;
    }
}
tag = document.createElement('style');
tag.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(tag);
if (iefail) {
    document.styleSheets[document.styleSheets.length - 1].cssText = css;
} else {
    tag[(typeof document.body.style.WebkitAppearance == 'string') /* webkit only */ ? 'innerText' : 'innerHTML'] = css;
}

};