/* ------------------------------------------------------------------------
 * application.js
 * Copyright (c) 2006-2007 37signals, LLC. All rights reserved.
 * ------------------------------------------------------------------------ */

Object.extend(Array.prototype, {
  returnFirstApplication: function(iterator) {
    var result;
    this.each(function(value) {
      result = iterator(value);
      if (result) throw $break;
    })
    return result;
  }
});

var MessageTransformers = {
  applyFirst: function(text) {
    return [ImageAutolink, YoutubeVideoAutolink, Autolink].returnFirstApplication(function(transformer) {
      return transformer.transform(text);
    });
  }
};

var Autolink = {
  Patterns: {
    url:   /((href=(?:'|")?)?(https?:\/\/|www\.)(\S+)(\/(?:\S+))?)//*'*/, 
    email: /([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/
  },
  
  Linkers: {
    url: function(string, tagOptions, replacement) {
      return Autolink.replaceURLs(string, function(url, extra) {
        var text = (replacement || Prototype.K)(url);
        return '<a href="' + url + '"' + 
          Autolink.htmlForTagOptions(tagOptions) + '>' + 
          text + '</a>' + extra;
      });
    },
    
    email: function(string, tagOptions, replacement) {
      replacement = replacement || Prototype.K;
      return string.gsub(Autolink.Patterns.email, function(match) {
        return '<a href="mailto:' + match[1] + '"' + 
          Autolink.htmlForTagOptions(tagOptions) + '>' +
          replacement(match[1]) + '</a>';
      });
    }
  },

  all: function(string, tagOptions, replacement) {
    for (var name in Autolink.Linkers)
      string = Autolink.Linkers[name](string, tagOptions, replacement);
    return string;
  },
  
  htmlForTagOptions: function(tagOptions) {
    return $H(tagOptions || {}).map(function(pair) {
      return pair.key + '="' + pair.value + '"';
    }).join(' ');
  },
  
  replaceURLs: function(string, replacement) {
    var extra = {};
    function trim(string) {
      if (!string) return;
      var pattern = /([^-0-9A-Za-z\/]+)$/, match;
      if (match = string.match(pattern))
        string = string.replace(match, '');
      extra.value = (match || [])[1];
      return string;
    }
    
    return string.gsub(Autolink.Patterns.url, function(match) {
      var all = match[1], existingLink = match[2], scheme = match[3],
        domain = match[4], path = match[5];
      if (existingLink) return all;
      if (scheme == 'www.') all = 'http://' + all;
      all = trim(all), path = trim(path);
      return replacement(all, extra.value || '');
    });
  },
  
  transform: function(message) {
    return this.all(message.escapeHTML(), {target: '_blank'}, 
      function(text) { return text.truncate(50, '&hellip;') }
    )
  }
};

Object.extend(Autolink, Autolink.Linkers);

var ImageAutolink = {
  image_url_match: function(text) {
    return text.strip().match(/^(http\S+(?:jpe?g|gif|png))(\?.*)?$/i)
  },
  
  inline_image: function(url) {
    return '<a href="'  + url + '" class="image" target="_blank">' +
           '<img src="' + url + '" style="border: 0px" width="0" onload="loadInlineImage(this)" /></a>'; 
  },
  
  link: function(text, replacement) {
    var match = this.image_url_match(text);
    if (!match) return false;
    return replacement(match[1]);
  },
  
  transform: function(message) {
    return this.link(message, function(url) { return this.inline_image(url) }.bind(this))
  }
};

var YoutubeVideoAutolink = {
  inlineYoutubeVideo: function(url, id) {
    return ('<a href="#{url}" class="image youtube_video" target="_blank">' + 
      '<img src="http://img.youtube.com/vi/#{id}/0.jpg" /></a>').interpolate({ url: url, id: id });
  },
  
  link: function(text, replacement) {
    var url = text.strip();
    var match = url.match(/^(?:http\S+[Yy][Oo][Uu][Tt][Uu][Bb][Ee]\.[Cc][Oo][Mm]\/watch\?v=)([\w-]+)(?:\S*)$/);
    if (!match) return false;
    return replacement(url, match[1]);
  },
  
  transform: function(message) {
    return this.link(message, this.inlineYoutubeVideo.bind(this));
  }
};

/*--------------------------------------------------------------------------*/

Ajax.Popup = Class.create();
Object.extend(Object.extend(Ajax.Popup.prototype, Ajax.Updater.prototype), {
  initialize: function(url, options) {
    this.popup = this.createWindow(options || {});
    this.temporaryElement = Element.extend(document.createElement("div"));
    Ajax.Updater.prototype.initialize.call(this, this.temporaryElement, url, options);

    var onComplete = this.options.onComplete;
    this.options.onComplete = function() {
      if (this.popup.closed) return;
      onComplete.apply(this, arguments);
      this.popup.document.title = this.options.title || '';
      this.popup.document.body.appendChild(this.temporaryElement);
    }.bind(this);
  },
  
  createWindow: function(options) {
    var defaults = $H({width: 480, height: 320, scrollbars: 'yes', status: 'no', 
      toolbar: 'no', location: 'no', menubar: 'no', directories: 'no', resizable: 'yes'});
    var parameters = defaults.merge(options.window || {}).invoke('join', '=').join(',');
    
    var popup = window.open('', options.name || 'popup', parameters);
    popup.document.write('<html><body id="popup_body"></body></html>');
    popup.document.close();
    
    return popup;
  }
});

/*--------------------------------------------------------------------------*/

var ShowHide = Class.create();
ShowHide.prototype = {
  initialize: function(element, callbacks) {
    this.element   = element = $(element);
    this.effect    = element.getAttribute('effect') || 'slide';
    this.duration  = parseFloat(element.getAttribute('duration')) || 0.25;
    this.activeClassName = element.getAttribute('activeclassname') || 'active';
    this.callbacks = callbacks;
    this.active    = Element.visible(element);
    this.element.showHide = this;
  },
  
  togglers: function() {
    return $(document.body).select('.show_hide_toggler_' + this.element.id);
  },
  
  toggle: function() {
    if (this.callbacks.beforeToggle) this.callbacks.beforeToggle(this);
    Effect.toggle(this.element, this.effect, {duration: this.duration, 
      afterFinish: this.afterToggle.bind(this)});
    this.active = !this.active;
    this.togglers().concat(this.element).each(this.adjustClassName.bind(this));
  },
  
  show: function() {
    if (this.active) return;
    this.toggle();
  },
  
  hide: function() {
    if (!this.active) return;
    this.toggle();
  },
  
  adjustClassName: function(element) {
    Element[this.active ? 'addClassName' : 'removeClassName'](element, this.activeClassName);
  },
  
  afterToggle: function() {
    if (this.active) {
      // The effect sets "bottom: 0" for some reason. 
      // Get rid of it here or bad things will happen.
      this.element.writeAttribute("style", "");
      this.element.down().writeAttribute("style", "");
    }
    (this.callbacks.afterToggle || Prototype.K).call(null, this);
  }
}

/*--------------------------------------------------------------------------*/

var Hover = {
  EXIT_DELAY  : 600,
  HOVER_CLASS : 'hover',
  
  lastTimer   : null,
  lastCommand : null,
  inhibit     : false,
  
  clearCurrent: function() {
    if(!this.lastTimer) return
    clearTimeout(this.lastTimer)
    eval(this.lastCommand)
    this.lastTimer = this.lastCommand = null
  },
  
  endWith: function(command) {
    if(this.inhibit) return
    this.lastCommand = command
    this.lastTimer = setTimeout(command, this.EXIT_DELAY)
  },
  
  toggle: function(on, container, nubbin) {
    if(this.inhibit) return
    
    if(on) {
      if($(container)) Element.addClassName(container, this.HOVER_CLASS)
      if($(nubbin)) Element.show(nubbin)
    } else {
      if($(container)) Element.removeClassName(container, this.HOVER_CLASS)
      if($(nubbin)) Element.hide(nubbin)
    }
  }
}

/*--------------------------------------------------------------------------*/

var transcript = {
  hover: {
    begin: function(id) {
      Hover.clearCurrent()
      Hover.toggle(true, 'file-' + id, 'nubbin_file_' + id)
    },
    
    end: function(id, delay) {
      if(delay)
        Hover.endWith('transcript.hover.end(' + id + ')')
      else
        Hover.toggle(false, 'file-' + id, 'nubbin_file_' + id)
    }
  }
}

/*--------------------------------------------------------------------------*/

function $T() {
  return new Date().getTime();
}

function $P(value) {
  return parseInt(document.documentElement.clientWidth * value / 100.0);
}

Form.forElement = function(element) {
  element = $(element);
  while (element && element.tagName.toLowerCase() != 'form')
    element = element.parentNode;
  return element;
}

/*--------------------------------------------------------------------------*/

String.prototype.blank = function() {
  return !!this.match(/^\s*$/);
}

/*--------------------------------------------------------------------------*/

function toggleSubmit(submit_element) {
  $(submit_element).disabled = !this.checked;
}

function updateChartWithSelectedLevel(level, price) {
  $('selected_plan_price').innerHTML = price;
  $$('table.pricing tr.shaded').first().className = '';
  $(level).className = 'shaded';
}

function changeTheme(theme) {
  var pattern = new RegExp('/' + theme + '\\.css');
  $$('link').each(function(link) {
    if (link.href.match(pattern)) {
      link.disabled = false;
    } else if (link.title == 'Theme' && 
        (link.rel == 'Stylesheet' || !link.disabled)) {
      link.disabled = true;
    }
  });
}

function uploadLogo() {
  if (!$('upload').value) return false;
  Element.show('upload_form_progress');
  Element.hide('upload_form_contents');
  $('upload_form_tag').target = 'upload_target';
  $('upload_form_tag').submit();
  return false;
}

function loadInlineImage(image) {
  var maxWidth = arguments.callee.maxWidth || 300;
  $(image).setStyle({width: 'auto', visibility: 'hidden'});
  if (image.width > maxWidth) image.style.width = maxWidth + 'px';
  image.style.visibility = 'visible';
  
  if (!image.up('body.transcript'))
    if (window.chat && chat.windowmanager) 
      setTimeout(chat.windowmanager.scrollToBottom.bind(chat.windowmanager), 50);
}

/*--------------------------------------------------------------------------*/

(function() {
  var match = navigator.userAgent.match(/Pyro\/(\d+)$/);
  window.Pyro = match ? { Version: parseInt(match[1]) } : false;
})();

if (Pyro && Pyro.Version <= 24) {
  // Hide the room search field for Pyro <= 24
  document.write('<style type="text/css">body div#Sidebar form#search_form { display: none }</style>');
}

/*--------------------------------------------------------------------------*/

Ajax.Request.prototype.inspect = function() {
  var template = new Template("#<Ajax.Request method: #{method}, url: #{url}, body: #{body}>");
  return template.evaluate({ method: Object.inspect(this.method), 
    url: Object.inspect(this.url), body: Object.inspect(this.body) });
};

/*--------------------------------------------------------------------------*/

var Cookie = {
  get: function(name) {
    var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
    return (cookie ? unescape(cookie[2]) : null);
  },
  
  set: function(name, value, daysToExpire) {
    var attrs = '; path=/';
    if (daysToExpire != undefined) {
      var d = new Date();
      d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
      attrs += '; expires=' + d.toGMTString();
    }
    return (document.cookie = escape(name) + '=' + escape(value || '') + attrs);
  },
  
  remove: function(name) {
    var cookie = Cookie.get(name) || true;
    Cookie.set(name, '', -1);
    return cookie;
  }
};
