﻿if (typeof __MYLINGUAL != 'object') {
  var __MYLINGUAL = {};
}

__MYLINGUAL.debugAlert = function () {
};

__MYLINGUAL.localizeWithData = function (json, doc, browser) {
  this.updateStatus(browser, "");
  // do nothing unless translation is available
  if (typeof json != "object") {
    return;
  }
  // convert json to internal representation
  var mappings = {
    text: {},
    re: []
  };
  this.initCommandMappings(mappings);
  for (var n in json) {
    if (n.charAt(0) == '$') {
      this.compileCommand(mappings, n, json[n]);
    } else if (n.match(/^\/(\^.*\$)\/$/)) {
      var v = json[n];
      try {
        n = new RegExp(RegExp.$1);
      } catch (e) {
        this.debugAlert(this._f('mylingual.error.recompile', [ n ]));
        continue;
      }
      mappings.re.push([ n, v ]);
    } else {
      mappings.text[n] = json[n];
    }
  }
  this.postProcessCommandMappings(mappings);
  // check url patterns
  if (this.ifSkipURL(mappings, doc.location)) {
    this.updateStatus(browser, this._s('status.bannedpage'));
    return;
  }
  // translate
  if (typeof this.log == 'function') {
    this.log("Mylingual: translating: " + doc.location);
  }
  this.localizeElement(doc.body, mappings, true);
  this.updateStatus(browser, this._s('status.translated'));
  // build handler for handilng DHTML modifs.
  var handler = function (evt) {
    if (handler.underOperation) {
      return;
    }
    if (typeof __MYLINGUAL.log == 'function') {
      var msg = (function (t) {
        if (t.id) {
          return "id='" + t.id + "'";
        } else if (t.className) {
          return "class='" + t.className + "'";
        } else if (t.parentNode) {
          return arguments.callee(t.parentNode);
        } else if (t.nodeType == 9) {
          return "no identifier at root";
        } else {
          return "not within document";
        }
      }).call(null, evt.target);
      msg += (function (t) {
        while (typeof t == 'object' && t.nodeType != 9) {
          t = t.parentNode;
        }
        if (! t) {
          return '';
        }
        return ", " + t.location;
      }).call(null, evt.target);
      __MYLINGUAL.log("Japanize: " + msg);
    }
    setTimeout(
      function () {
        handler.underOperation = true;
        __MYLINGUAL.localizeElement(
          evt.target,
          mappings,
          __MYLINGUAL.getElementTranslationMode(
            mappings, doc.body, evt.target.parentNode));
        handler.underOperation = false;
      },
      1);
  };
  if (doc.addEventListener) {
    doc.addEventListener("DOMNodeInserted", handler, false);
    doc.addEventListener("DOMCharacterDataModified", handler, false);
    doc.addEventListener(
      "DOMAttrModified",
      function (evt) {
        if (evt.target.tagName == 'INPUT' || evt.target.tagName == 'OPTION') {
          handler(evt);
        }
      },
      false);
  }
};

__MYLINGUAL.translateText = function (orig, mappings) {
  // direct match
  if (typeof mappings.text[orig] != 'undefined') {
    return mappings.text[orig];
  }
  // match (while taking care of surrounding spaces)
  if (orig.match(/^([ \r\n\t\xa0]*)(.+?)([ \r\n\t\xa0]*)$/)
    && (RegExp.$1 != '' || RegExp.$3 != '')) {
    if (typeof mappings.text[RegExp.$2] != 'undefined') {
      return RegExp.$1 + mappings.text[RegExp.$2] + RegExp.$3;
    }
  }
  // regexp
  for (var i = 0; i < mappings.re.length; i++) {
    var m = null;
    if (m = orig.match(mappings.re[i][0])) {
      return this._gsub(
        mappings.re[i][1],
        /\$(R?)([1-9])/g,
        function (_dummy, rerun, digit) {
          var t = m[digit - 0];
          if (rerun) {
            var t2 = __MYLINGUAL.translateText(t, mappings);
            if (t2 != null) {
              t = t2;
            }
          }
          return t;
        });
    }
  }
  return null;
};

__MYLINGUAL.initCommandMappings = function (mappings) {
  var f0 = function () {
    return {
      re: [],
      reCaseless: [],
      text: {}
    }
  };
  var f1 = function () {
    return {
      'class': f0(),
      id:      f0(),
      path:    f0(),
      tag:     f0(),
      url:     f0()
    };
  };
  mappings.skip = f1();
  mappings.translate = f1();
};

__MYLINGUAL.postProcessCommandMappings = function (mappings) {
  var f0 = function (t, n, f) {
    t[n] = t[n].length != 0 ? new RegExp(t[n].join('|'), f) : undefined;
  };
  var f1 = function (t) {
    f0(t, 're', '');
    f0(t, 'reCaseless', 'i');
  };
  var f2 = function (t) {
    f1(t['class']);
    f1(t.id);
    f1(t.path);
    f1(t.tag);
    f1(t.url);
  };
  f2(mappings.skip);
  f2(mappings.translate);
};

__MYLINGUAL.compileCommand = function (mappings, name, value) {
  if (! name.match(/^\$(.*?)\(\s*(~{0,2})\s*(.*)\s*\)$/)) {
    return;
  }
  var type = RegExp.$1;
  var re = RegExp.$2 ? RegExp.$2.length : 0;
  var match = RegExp.$3;
  var store = mappings
    [value[0] == 'skip' || value[0] == '' ? 'skip' : 'translate']
    [type];
  if (typeof store != 'object') {
    return;
  }
  if (re) {
    if (! new RegExp(match, re == 2 ? 'i' : '')) {
      this.debugAlert('Syntax error: ' + name);
      return;
    }
    store[re == 2 ? 'reCaseless' : 're'].push(match);
  } else {
    if (type == 'tag') {
      match = match.toUpperCase();
    }
    store.text[match] = 1;
  }
};

__MYLINGUAL.ifSkipURL = function (mappings, loc) {
  return this.translateOrSkip(mappings.skip.path, loc.pathname)
    || this.translateOrSkip(mappings.skip.url, loc.toString());
};

__MYLINGUAL.translateOrSkipElement = function (mappings, e, current) {
  var table = mappings[current ? 'skip' : 'translate'];
  if (this.translateOrSkip(table.tag, e.tagName)
    || e.className && this.translateOrSkip(table['class'], e.className)
    || e.id && this.translateOrSkip(table.id, e.id)) {
    return ! current;
  }
  return current;
};

__MYLINGUAL.translateOrSkip = function (table, value) {
  value = value.toString();
  return typeof table.text[value] != 'undefined'
    || (table.re && value.match(table.re))
    || (table.reCaseless && value.match(table.reCaseless));
};

__MYLINGUAL.getElementTranslationMode = function (mappings, body, element) {
  if (element == null) {
    return false;
  }
  var path = [];
  for (var p = (element.nodeType == 1 ? element : element.parentNode);
    p != body;
    p = p.parentNode) {
    if (p == null) {
      return false;
    }
    path.push(p);
  }
  var translate = true;
  while (path.length != 0) {
    translate = this.translateOrSkipElement(mappings, path.pop(), translate);
  }
  return translate;
};

__MYLINGUAL.localizeElement = function (node, mappings, translate) {
  if (node.nodeType == 1) {
    translate = this.translateOrSkipElement(mappings, node, translate);
    if (node.nodeName == "SCRIPT" || node.nodeName == "STYLE") {
      // nothing to do
      return;
    } else if (node.nodeName == "INPUT") {
      if (! translate) {
        return;
      }
      if (node.type == "button" || node.type == "reset") {
        var translated = this.translateText(node.value, mappings);
        if (translated != null) {
          node.value = translated;
        }
      }
      return;
    } else if (node.nodeName == "OPTION") {
      if (! translate) {
        return;
      }
      var translated = this.translateText(node.text, mappings);
      if (translated != null) {
        node.value = node.value.toString();
        node.text = translated;
      }
      return;
    }
    var children = node.childNodes;
    for (var i = 0; i < children.length; i++) {
      this.localizeElement(children.item(i), mappings, translate);
    }
  } else if (translate && node.nodeType == 3) {
    var translated = this.translateText(node.nodeValue, mappings);
    if (translated != null) {
      node.nodeValue = translated;
    }
  }
};

// hacks for user.js
__MYLINGUAL.updateStatus = function (_dummy1, text) {
  if (text) {
    var t = document.createElement('div');
    t.id = '__mylingual_status';
    (function (o) {
      for (var i in o) {
        t.style[i] = o[i];
      }
    })({
      border:     '1px solid #666',
      background: '#f88',
      padding:    '0.3em',
      color:      'black',
      fontFamily: 'sans-serif',
      fontWeight: 'bold',
      position:    window.innerWidth ? 'fixed' : 'absolute',
      left:        '10px',
      bottom:      '10px',
      zIndex:      100
    });
    document.body.appendChild(t);
    t.innerHTML = 'Mylingual: ' + text;
    window.setTimeout(
      function () {
        t.parentNode.removeChild(t);
      },
      3000);
  }
};

__MYLINGUAL._s = function (n) {
  var s = {
    'status.translated': 'Translated',
    'status.bannedpage': 'Preserving Original'
  }[n];
  if (typeof s != 'undefined' && typeof this._s.table[s] != 'undefined') {
    s = this._s.table[s];
  }
  return s;
};

__MYLINGUAL._s.table = {
};

__MYLINGUAL._f = function () {};

__MYLINGUAL.localize = function (json, stringtable) {
  if (typeof json != 'object') return;
  if (typeof stringtable == 'object') {
    this._s.table = stringtable;
  }
  this.localizeWithData(json, document, {});
};

// flickr uses customized String.prototype.replace
__MYLINGUAL._gsub = function (str, re, func) {
  return str.replace(re, func);
};
if ("0a1".replace(/[a-z]/g, function (m) { return "A"; }) != "0A1") {
  __MYLINGUAL._gsub = function (str, re, func) {
    var out = "";
    var match;
    var start = re.lastIndex = 0;
    while (match = re.exec(str)) {
      out += str.slice(start, match.index);
      out += func.apply(null, match).toString();
      start = re.lastIndex;
    }
    out += str.substring(start);
    return out;
  };
}

(function () {
  // determine language
  var lang = (function () {
    var self = document.getElementById('mylingual-core');
    if (self && self.src.match(/[?&;]lang=([a-z\-]+)/)) {
      return RegExp.$1;
    }
    return 'ja';
  })();
  var elem = document.createElement('script');
  elem.src =
    'http://data.mylingual.net/data_jsonp/'
    + lang
    + '/'
    + location.host
    + '?jsonp=__MYLINGUAL.localize';
  document.body.appendChild(elem);
})();
