// The following are instance methods and variables
var Note = Class.create({
  initialize: function(id, is_new, raw_body) {
    if (Note.debug) {
      console.debug("Note#initialize (id=%d)", id)
    }

    this.id = id
    this.is_new = is_new
    this.document_observers = [];

    // Cache the elements
    this.elements = {
      box: $('note-box-' + this.id),
      corner: $('note-corner-' + this.id),
      body: $('note-body-' + this.id),
      image: $('image')
    }

    // Cache the dimensions
    this.fullsize = {
      left: this.elements.box.offsetLeft,
      top: this.elements.box.offsetTop,
      width: this.elements.box.clientWidth,
      height: this.elements.box.clientHeight
    }

    // Store the original values (in case the user clicks Cancel)
    this.old = {
      raw_body: raw_body,
      formatted_body: this.elements.body.innerHTML
    }
    for (p in this.fullsize) {
      this.old[p] = this.fullsize[p]
    }

    // Make the note translucent
    if (is_new) {
      this.elements.box.setOpacity(0.2)
    } else {
      this.elements.box.setOpacity(0.5)
    }

    if (is_new && raw_body == '') {
      this.bodyfit = true
      this.elements.body.style.height = "100px"
    }

    // Attach the event listeners
    this.elements.box.observe("mousedown", this.dragStart.bindAsEventListener(this))
    this.elements.box.observe("mouseout", this.bodyHideTimer.bindAsEventListener(this))
    this.elements.box.observe("mouseover", this.bodyShow.bindAsEventListener(this))
    this.elements.corner.observe("mousedown", this.resizeStart.bindAsEventListener(this))
    this.elements.body.observe("mouseover", this.bodyShow.bindAsEventListener(this))
    this.elements.body.observe("mouseout", this.bodyHideTimer.bindAsEventListener(this))
    this.elements.body.observe("click", this.showEditBox.bindAsEventListener(this))

    this.adjustScale()
  },

  // Returns the raw text value of this note
  textValue: function() {
    if (Note.debug) {
      console.debug("Note#textValue (id=%d)", this.id)
    }

    return this.old.raw_body.strip()
  },

  // Removes the edit box
  hideEditBox: function(e) {
    if (Note.debug) {
      console.debug("Note#hideEditBox (id=%d)", this.id)
    }

    var editBox = $('edit-box')

    if (editBox != null) {
      var boxid = editBox.noteid

      $("edit-box").stopObserving()
      $("note-save-" + boxid).stopObserving()
      $("note-cancel-" + boxid).stopObserving()
      $("note-remove-" + boxid).stopObserving()
      $("note-history-" + boxid).stopObserving()
      $("edit-box").remove()
    }
  },

  // Shows the edit box
  showEditBox: function(e) {
    if (Note.debug) {
      console.debug("Note#showEditBox (id=%d)", this.id)
    }

    this.hideEditBox(e)

    var insertionPosition = Note.getInsertionPosition()
    var top = insertionPosition[0]
    var left = insertionPosition[1]
    var html = ""

    html += '<div id="edit-box" style="top: '+top+'px; left: '+left+'px; position: absolute; visibility: visible; z-index: 100; background: white; border: 1px solid black; padding: 12px;">'
    html += '<form onsubmit="return false;" style="padding: 0; margin: 0;">'
    html += '<textarea rows="7" id="edit-box-text" style="width: 350px; margin: 2px 2px 12px 2px;">' + this.textValue() + '</textarea>'
    html += '<input type="submit" value="Save" name="save" id="note-save-' + this.id + '">'
    html += '<input type="submit" value="Cancel" name="cancel" id="note-cancel-' + this.id + '">'
    html += '<input type="submit" value="Remove" name="remove" id="note-remove-' + this.id + '">'
    html += '<input type="submit" value="History" name="history" id="note-history-' + this.id + '">'
    html += '</form>'
    html += '</div>'

    $("note-container").insert({bottom: html})
    $('edit-box').noteid = this.id
    $("edit-box").observe("mousedown", this.editDragStart.bindAsEventListener(this))
    $("note-save-" + this.id).observe("click", this.save.bindAsEventListener(this))
    $("note-cancel-" + this.id).observe("click", this.cancel.bindAsEventListener(this))
    $("note-remove-" + this.id).observe("click", this.remove.bindAsEventListener(this))
    $("note-history-" + this.id).observe("click", this.history.bindAsEventListener(this))
    $("edit-box-text").focus()
  },

  // Shows the body text for the note
  bodyShow: function(e) {
    if (Note.debug) {
      console.debug("Note#bodyShow (id=%d)", this.id)
    }

    if (this.dragging) {
      return
    }

    if (this.hideTimer) {
      clearTimeout(this.hideTimer)
      this.hideTimer = null
    }

    if (Note.noteShowingBody == this) {
      return
    }

    if (Note.noteShowingBody) {
      Note.noteShowingBody.bodyHide()
    }

    Note.noteShowingBody = this

    if (Note.zindex >= 9) {
      /* don't use more than 10 layers (+1 for the body, which will always be above all notes) */
      Note.zindex = 0
      for (var i=0; i< Note.all.length; ++i) {
        Note.all[i].elements.box.style.zIndex = 0
      }
    }

    this.elements.box.style.zIndex = ++Note.zindex
    this.elements.body.style.zIndex = 10
    this.elements.body.style.top = 0 + "px"
    this.elements.body.style.left = 0 + "px"

    var dw = document.documentElement.scrollWidth
    this.elements.body.style.visibility = "hidden"
    this.elements.body.style.display = "block"
    if (!this.bodyfit) {
      this.elements.body.style.height = "auto"
      this.elements.body.style.minWidth = "140px"
      var w = null, h = null, lo = null, hi = null, x = null, last = null
      w = this.elements.body.offsetWidth
      h = this.elements.body.offsetHeight
      if (w/h < 1.6180339887) {
        /* for tall notes (lots of text), find more pleasant proportions */
        lo = 140, hi = 400
        do {
          last = w
          x = (lo+hi)/2
          this.elements.body.style.minWidth = x + "px"
          w = this.elements.body.offsetWidth
          h = this.elements.body.offsetHeight
          if (w/h < 1.6180339887) lo = x
          else hi = x
        } while ((lo < hi) && (w > last))
      } else if (this.elements.body.scrollWidth <= this.elements.body.clientWidth) {
        /* for short notes (often a single line), make the box no wider than necessary */
        // scroll test necessary for Firefox
        lo = 20, hi = w

        do {
          x = (lo+hi)/2
          this.elements.body.style.minWidth = x + "px"
          if (this.elements.body.offsetHeight > h) lo = x
          else hi = x
        } while ((hi - lo) > 4)
        if (this.elements.body.offsetHeight > h)
          this.elements.body.style.minWidth = hi + "px"
      }

      if (Prototype.Browser.IE) {
        // IE7 adds scrollbars if the box is too small, obscuring the text
        if (this.elements.body.offsetHeight < 35) {
          this.elements.body.style.minHeight = "35px"
        }

        if (this.elements.body.offsetWidth < 47) {
          this.elements.body.style.minWidth = "47px"
        }
      }
      this.bodyfit = true
    }
    this.elements.body.style.top = (this.elements.box.offsetTop + this.elements.box.clientHeight + 5) + "px"
    // keep the box within the document's width
    var l = 0, e = this.elements.box
    do { l += e.offsetLeft } while (e = e.offsetParent)
    l += this.elements.body.offsetWidth + 10 - dw
    if (l > 0)
      this.elements.body.style.left = this.elements.box.offsetLeft - l + "px"
    else
      this.elements.body.style.left = this.elements.box.offsetLeft + "px"
    this.elements.body.style.visibility = "visible"
  },

  // Creates a timer that will hide the body text for the note
  bodyHideTimer: function(e) {
    if (Note.debug) {
      console.debug("Note#bodyHideTimer (id=%d)", this.id)
    }
    this.hideTimer = setTimeout(this.bodyHide.bindAsEventListener(this), 250)
  },

  // Hides the body text for the note
  bodyHide: function(e) {
    if (Note.debug) {
      console.debug("Note#bodyHide (id=%