/
ColdWeb-3.0-ALPHA/
ColdWeb-3.0-ALPHA/src/
// -----------------------------------------------------------------
//
// String Library
//
// -----------------------------------------------------------------

new object $string: $root;

public method .trim(): native {
  arg str, [dir];
  var reg, range;
  
  str = strsed("^ *", str, "");
  return strsed(" *$", str, "");
};

public method .is_numeric() {
    arg string;
    
    return toint(string) || string == "0";
};

public method .strip() {
    arg str;

    return strsed("[^a-z0-9_]", str, "", "g") || str;
};

public method .chop() {
    arg str, len, [end];
    
    // chops string off strlen(end) characters before len and appends len
    end = [@end, "..."][1];
    if (strlen(str) < len)
        return str;
    if (strlen(str) < strlen(end))
        return str;
    return pad(str, len - strlen(end)) + end;
};

public method .rindex() {
    arg str, c;
    var i;

    i = strlen(str);
    while (i) {
        if (str[i] == c)
            return i;
        i = i - 1;
    }
    return 0;
};

public method .to_list() {
    arg str;

    str = .strip(str);

    return (> tosym(str) <);
};

// ------------------------------
//
// here on down is web-specific library 'schtuff'
//

public method .break_fields() {
    arg args;
    var fields, field, values;
                    
    fields = #[]; 
    for field in (args.explode("&")) {
        field = field.explode("=");
        if (listlen(field) == 1)
            field = [field[1], ""];
        fields = fields.add(@field);
    }
    return fields;
};
           
public method .explode_url() {
    arg line;
    var out, args, i;

    i = "?" in line;
    if (i) {
        args = substr(line, i + 1);
        line = substr(line, 1, i - 1);
    }
    if (args)
        out = #[['path, explode(line, "/")], ['args, .break_fields(args)]];
    else
        out = #[['path, explode(line, "/")], ['args, #[]]];
    return out;
};

public method .filter_text() {
    arg text;
    var x, line;

    for x in [1 .. listlen(text)] {
        if (text[x]) {
            line = text[x].replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
            text = replace(text, x, line);
        }
    }
    return text;
};
  
public method .filter_line() {
    arg line;
 
    return line.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
};

// -----------------------------------------------------------------
//
// Dictionary library
//
// -----------------------------------------------------------------

new object $dictionary: $root;

public method .to_list() {
    arg dict;
    var list, x, k;
    
    // merges into an associated list.
    k = dict_keys(dict);
    list = [];
    for x in (k)
        list = [@list, [x, dict[x]]];
    return list;
};

public method .union() {
    arg dict1, dict2;
    var key;
    
    // like union() but for dictionaries.  adds any keys from dict2 that don't
    // already exist in dict1 to dict1 and returns the result.  Order of keys in
    // result is not guaranteed.
    for key in (dict1)
        dict2 = dict_add(dict2, key[1], key[2]);
    return dict2;
};

public method .values() {
    arg dict;
    var list, x, k;
    
    // returns values same as dict_keys() returns keys.
    k = dict_keys(dict);
    list = [];
    for x in (k)
        list = [@list, dict[x]];
    return list;
};

public method .replace() {
    arg dict, key, value;
    
    dict = (> dict_del(dict, key) <);
    dict = (> dict_add(dict, key, value) <);
    return dict;
};

public method .invert() {
    arg dict;
    var inverted, x;
    
    // Invert a dict (keys<->values)
    inverted = #[];
    for x in (dict_keys(dict))
        inverted = dict_add(inverted, dict[x], x);
    return inverted;
};

public method .add_elem() {
    arg dict, key, elem;
    var value;
    
    // same as old dict_add_elem
    value = (| dict[key] |);
    if (type(value) != 'list && type(value) != 'error)
        throw(~type, "Value for key " + key + " (" + value + ") is not a list.");
    if (value)
        value = [@value, elem];
    else
        value = [elem];
    return dict_add(dict, key, value);
};

public method .del_elem() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (type(value) != 'list && type(value) != 'error)
        throw(~type, "Value for key " + key + " (" + value + ") is not a list.");
    value = setremove(value, elem);
    if (!value)
        return dict_del(dict, key);
    return dict_add(dict, key, value);
};

public method .add(): native;
public method .del(): native;
public method .keys(): native;
public method .contains(): native;

new object $list: $root;

public method .graft_strings() {
    arg list;
    var word, out, i;

    out = [];
    word = "";
    for i in (list) {
        if (type(i) != 'symbol) {
            word += i;
        } else {
            out += [word, i];
            word = "";
        }
    }
    if (word)
        out += [word];
    return out;
};

public method .prefix() {
  arg list, prefix;
  var out, elem;
  
  out = [];
  for elem in (list)
      out = out + [prefix + elem];
  return out;
};

// Combines l1 and l2 by appending the first element of l2 to the last
// of l1.

public method .graft() {
  arg l1, l2;
  var last, first;
  
  if (type(l2) != 'list)
      l2 = [l2];
  last = (| l1.last() |) || "";
  first = (| l2[1] |) || "";
  l1 = [@l1.chop(), last + first];
  if (l2.length() > 1)
      l1 = l1 + sublist(l2, 2);
  return l1;
};

public method .to_string() {
    arg list, [sep];
    var str, part;
    
    if (!list)
        return "";
    sep = [@sep, " "][1];
    str = list[1];
    for part in (delete(list, 1))
        str = str + sep + part;
    return str;
};

public method .to_english() {
    arg list, [options];
    var empty, and, sep;
    
    empty = [@options, "nothing"][1];
    switch (listlen(list)) {
        case 0:
            return empty;
        case 1:
            return tostr(list[1]);
    }
    and = [@options, " and ", " and "][2];
    sep = [@options, ", ", ", ", ", "][3];
    return .to_string(delete(list, listlen(list)), sep) + and + list[listlen(list)];
};

public method .map() {
    arg list, method, [args];
    var out, x;
    
    // call 'method on each object, return results.
    out = [];
    for x in (list)
        out = [@out, x.(method)(@args)];
    return out;
};

public method .filter() {
    arg list, method, [args];
    var out, x;
    
    // similar to .map, but returns a list of objects which returned a
    // true value from 'method.
    out = [];
    for x in (list) {
        if (x.(method)(@args))
            out = [@out, x];
    }
    return out;
};


public method .element_maxlength() {
  arg list;
  var elm, max, len;
  
  for elm in (list) {
      len = tostr(elm).length();
      if (len > max)
          max = len;
  }
  return max;
};

public method .col2() {
    arg list, [args];
    var lines, line, l1, l2, l, x, out, diff, sep, nl;

    sep = [@args, "     "][1];
    nl = [@args, "\n", "\n"][2];
    l = listlen(list);
    if (l == 1)
        return list;

    // if it is an odd length, add one to it (it rounds down, and we want up)
    if (l % 2)
        diff = 1;
    l = (l / 2);
    l1 = sublist(list, 1, l + diff);
    l2 = sublist(list, l + diff + 1);
    out = [];
    for x in [1 .. l]
        out = [@out, l1[x] + sep + l2[x] + nl];
    if (diff)
        out = [@out, l1[l+1] + nl];
    return out;
};

public method .lcolumnize() {
  arg list, [args];
  var line, part, lines, max, cols, col, width, len, sep;
  
  len = [@args, 79][1];
  sep = [@args, "", ""][2];
  lines = [];
  line = "";
  max = .element_maxlength(list) + sep.length();
  cols = len > max ? len / max | 1;
  width = len / cols - sep.length();
  col = cols;
  for part in (list) {
      col = col - 1;
      if (!col) {
          lines = lines + [line + part];
          line = "";
          col = cols;
          continue;
      }
      line = line + pad(part, width);
  }
  if (line)
      return lines + [line];
  return lines;
};

public method .columnize() {
    arg list, cols, [rest];
    var width, lines, line, separator, linelength, curcol;
    
    // turn [...] into ".   .   ."
    // rest[1]==separator; rest[2]==linelength
    separator = [@rest, "   "][1];
    linelength = [@rest, 78, 78][2];
    width = linelength / cols - strlen(separator);
    lines = [];
    while (list) {
        line = pad(list[1], width);
        list = sublist(list, 2);
        for curcol in [2 .. cols] {
            if (list) {
                line = line + separator + pad(list[1], width);
                list = sublist(list, 2);
            }
        }
        lines = [@lines, line];
    }
    return lines;
};

public method .reverse() {
    arg list;
    var elm, reversed;
    
    // .reverse(list)
    // -> list with its elements reversed
    reversed = [];
    for elm in (list)
        reversed = [elm, @reversed];
    return reversed;
};

public method .compress() {
    arg list;
    var out, last, x;
    
    // [a,a,b,b,c,c,d,d] => [a,b,c,d]
    // removes duplicate entries in a list
    out = [];
    for x in (list) {
        if (!(x in out))
            out = [@out, x];
    }
    return out;
};

public method .last() {
    arg list;
    
    return list[listlen(list)];
};

public method .count() {
    arg elem, list;
    var count;
    
    // count of elem in list
    while (elem in list) {
        count = count + 1;
        list = sublist(list, (elem in list) + 1);
    }
    return count;
};

public method .numbered_text() {
    arg text;
    var line;
    
    // returns that list with line numbers pre-pended
    for line in [1 .. listlen(text)]
        text = replace(text, line, (line < 10 ? " " | "") + line + ": " + text[line]);
    return text;
};

public method .slice() {
    arg big_list, element;
    var list, ret_list;
    
    // Return elementh' element of all lists in big_list
    // No type or length checking done for speed purposes.
    ret_list = [];
    for list in (big_list)
        ret_list = [@ret_list, list[element]];
    return ret_list;
};

public method .swap() {
    arg list, a, b;
    var holder;
    
    // swap elements at indexes a and b
    holder = (> list[a] <);
    list = (> replace(list, a, list[b]) <);
    list = replace(list, b, holder);
    return list;
};

public method .max() {
    arg list;
    
    // return greatest element of list (no type checking performed)
    // if list is [], returns 0
    while (listlen(list) > 1) {
        if (list[1] > list[2])
            list = delete(list, 2);
        else
            list = delete(list, 1);
    }
    return [@list, 0][1];
};

public method .min() {
    arg list;
    
    // return least element of list (no type checking performed)
    // if list is [], returns 0
    while (listlen(list) > 1) {
        if (list[1] < list[2])
            list = delete(list, 2);
        else
            list = delete(list, 1);
    }
    return [@list, 0][1];
};

public method .flatten() {
    arg list;
    var toret, elem;
    
    // [[[x], x], x]   =>   [x, x, x]
    toret = [];
    for elem in (list) {
        if (type(elem) == 'list)
            toret = toret + .flatten(elem);
        else
            toret = toret + [elem];
    }
    return toret;
};

public method .msort() {
  arg list, [keys];
  
  keys = keys ? keys[1] | list;
  if (listlen(list) != listlen(keys))
      throw(~invarg, "Invalid key list - the list lengths must be the same.");
  if (!list)
      return [];
  return (._merge_sort(list, keys))[1];
};

private method ._merge_sort() {
  arg list, keys;
  var i, j, l1, k1, l2, k2, n1, n2, n;
  
  n = listlen(list);
  if (n == 1)
      return [list, keys];
  n1 = n / 2;
  n2 = n - n1;
  l1 = ._merge_sort(list.subrange(1, n1), keys.subrange(1, n1));
  k1 = l1[2];
  l1 = l1[1];
  l2 = ._merge_sort(list.subrange(n1 + 1, n2), keys.subrange(n1 + 1, n2));
  k2 = l2[2];
  l2 = l2[1];
  list = [];
  keys = [];
  i = 1;
  j = 1;
  if (n > 30)
      pause();
  while (i <= n1 && j <= n2) {
      if (k1[i] <= k2[j]) {
          list = [@list, l1[i]];
          keys = [@keys, k1[i]];
          i = i + 1;
      } else {
          list = [@list, l2[j]];
          keys = [@keys, k2[j]];
          j = j + 1;
      }
  }
  return [[@list, @l1.subrange(i), @l2.subrange(j)], [@keys, @k1.subrange(i), 
 @k2.subrange(j)]];
};

public method .subrange(): native;

public method .sum() {
    arg ints;
    var ret;
    
    // returns a sum of each integer in the list.
    ret = 0;
    while (ints) {
        ret = ret + ints[1];
        ints = delete(ints, 1);
    }
    return ret;
};

public method .to_buffer() {
    arg list;
    
    // assumes this is a list of strings (for now)
    return strings_to_buf(list);
};

public method .delete(): native;
public method .replace(): native;

public method .chop() {
    arg list, [count];
    
    // chops the last <count> elements off the list.
    // return [] if count is longer then the list.
    count = count || 1;
    return (| sublist(list, 1, listlen(list) - count) |) || [];
};

public method .length(): native;

public method .union(): native;

public method .del() {
    arg list, element;
    
    return (> setremove(list, element) <);
};

public method .add() {
    arg list, element;
    
    return (> setadd(list, element) <);
};

public method .tb_to_html() {
    arg t, status;
    var line, out, x;
    
    out = "<h2>" + t[1][2].filter_line() + "</h2>";
    out += "<i><b>Thrown by " + ._html_traceback(@t[2]) + "</b></i>\n<p>"; 
    for x in [3 .. listlen(t)]
        out += "<code><i>" + t[x][1] + "</i>: " + ._traceback(@t[x]) + "</code><br>";
    return $sys.response(status, out + "</p>");
};

public method .tb_to_text() {
    arg t; 
    var line, out, x;

    out = "=> " + t[1][2];
    out = [out, "Thrown by " + ._traceback(@t[2])];
    for x in [3 .. listlen(t)]
        out = [@out, t[x][1] + ": " + ._traceback(@t[x])];
    return out;
};
 
private method ._html_traceback() {
    arg type, what, [more];
    var line;
    
    switch (type) {
        case 'function:
            return "function <tt>" + what + "()</tt>";
        case 'opcode:
            return "operator <tt>" + what + "</tt>";
        default:
            line = "method <tt>" + more[2] + "." + tostr(what) + "()</tt>";
            if (more[1] != more[2])
                line = line + " (" + more[1] + ")"; 
            line = line + " line " + more[3];
            return line;
    }
};

private method ._traceback() {
    arg type, what, [more];
    var line;

    switch (type) {
        case 'function:
            return "function " + what + "()";
        case 'opcode:
            return "operator " + what;
        default:   
            line = "method " + more[2] + "." + tostr(what) + "()";
            if (more[1] != more[2])
                line = line + " (" + more[1] + ")";
            line = line + " line " + more[3];
            return line;
    }
};

new object $time: $root;
new object $http: $root;