// Wiki provides user strage on a WebDAV server or local storage.
// It depends on JQuery
//
// Usage:
// var wiki = new Wiki()
// wiki.contentsChanged = function(aString) { alert(aString) }
// wiki.save("Hello, world!")
// window.onload = function() { wiki.init() }

function Wiki() {
  // location of data
  this.storage = "data";
  // suffix of data file
  this.suffix = ".js";
  // name of default project
  this.defaultTitle = "Home";
  // callback function which to know if content is changed.
  this.isDirty = function() { return false; };
  // callback function called when URL is changed
  this.contentsChanged = function(aString) { alert("Contents: " + aString); };
  // interval for checking the url in millisecond
  this.interval = 500;
  this.auth = null;

  // private
  this.oldHash = null;
}

Wiki.prototype.init = function () {
  var self = this;
  var hashChangedHandler = function() {
    if (document.location.hash == self.oldHash)
      return;

    if (document.location.hash == "") {
      if (self.oldHash == null) {
        document.location.hash = "#" + self.defaultTitle;
      } else {
        document.location.hash = self.oldHash;
        return;
      }
    }
    self.oldHash = document.location.hash;
    self.loadProject();
  };
  this.intervalId = setInterval(hashChangedHandler, this.interval);

  window.onbeforeunload = function() {
    if (self.isDirty()) return dirtyAreYouSureMessage;
  };
  hashChangedHandler();
}

// Answer current title of the project.
Wiki.prototype.title = function () {
  return this._title.replace(/_/g, " ");
};

// Answer current file name.
Wiki.prototype.fileName = function() {
  return this._title + this.suffix;
}

Wiki.prototype.dirtyAreYouSureMessage = "The changes you have made to this project will be lost unless you press 'cancel' " +
  "and save your work. Proceed?";

Wiki.prototype.loadProject = function() {
  if (arguments.length > 0) {
    if (arguments[0] == "" || "#" + arguments[0] == document.location.hash) return;
    document.location.hash = this.oldHash = "#" + arguments[0];
  }
  if (this.isDirty() && !confirm(this.dirtyAreYouSureMessage)) return;
  var projName = document.location.hash.substring(1);

  var result = this.readUrl(this.urlFor(projName));
  this._title = projName;
  this.contentsChanged(result);
}

Wiki.prototype.save = function(aString) {

  try {
    var projName = document.location.hash.substring(1);

    // the following is an ugly hack to fix a bug in prototype.js
    if (aString == "")
      aString = " ";
    this.writeUrl(this.urlFor(projName), aString);
  }
  catch (e) { // This is not called by callback but good for locak disk.
    alert("Error: " + e + "\n" +
          "Please save your work locally (by cutting and pasting),\n" +
          "and let Alex know about this problem.");
    throw e;
  }
}

// Convert from a project name to URL
Wiki.prototype.urlFor = function(projName) {
  return this.storage + "/" + projName + this.suffix;
}

// Library dependent code: PUT
Wiki.prototype.writeUrl = function(url, text) {

  if (document.location.protocol == "file:") {
    return this.saveFileWithMozilla(url, text);
  };

  var auth = this.auth;
  $.ajax({
	   type: "PUT",
	   url: url,
	   data: text,
	   async: false,
	   beforeSend: function(req) {
	     if (auth) req.setRequestHeader("Authorization", "Basic " + auth);
	   },
	   error: function (req, status) { throw req.responseText; }
	 });
}

// Library dependent code: GET
Wiki.prototype.readUrl = function(url) {
  var beforeSend = function (xhr) {
    xhr.setRequestHeader("If-Modified-Since", "Thu, 01 Jan 1970 00:00:00 GMT");
  };
  var result = null;
  try { // Catch an error from mozilla API for a local directory.
      $.ajax({
	   type: "GET",
	   url: url,
	   async: false,
	   beforeSend: beforeSend,
	   success: function (data, status) { result = data; },
	   error: function (req, status, e) { /* DO NOTHING */ }
	 });
  } catch(e) {
    return null;
  }
  return result;
}

// http://ask.metafilter.com/34651/Saving-files-with-Javascript
Wiki.prototype.saveFileWithMozilla = function(url, content) {
  // very naive path conversion
  var filePath;
  var pathname = location.pathname;
  if(pathname.charAt(2) == ":") {
	var directory = pathname.replace(/[^/]*$/, "").slice(1);
	filePath = (directory + url).replace(new RegExp("/","g"),"\\");
  } else {
	var directory = location.pathname.replace(/[^/]*$/, "");
	filePath = (directory + url);
  }

  if(window.Components) {
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
    file.initWithPath(filePath);
    if (!file.exists())
      file.create(0, 0664);
    var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
    out.init(file, 0x20 | 0x02, 00004,null);

    var s=mozConvertUnicodeToUTF8(content);
    out.write(s, s.length);
    out.flush();
    out.close();
  } else {
    throw "window.Content is not found.";
  }
};

// Followed source code is derived from Tiddlywiki http://www.tiddlywiki.com/
//
// TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com)
// Copyright (c) UnaMesa Association 2004-2008

function mozConvertUnicodeToUTF8(s)
{
	try {
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
		converter.charset = "UTF-8";
	} catch(ex) {
		return manualConvertUnicodeToUTF8(s);
	} // fallback
	var u = converter.ConvertFromUnicode(s);
	var fin = converter.Finish();
	return fin.length > 0 ? u + fin : u;
}

function manualConvertUnicodeToUTF8(s)
{
	return unescape(encodeURIComponent(s));
}

