// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
var Globals = {
  EnableFancyEffects: false,
  EffectDuration: 0.30,
  FastEffectDuration: 0.2,
  FlashDelay: 0.5,
  Debug: true
}

var Browser = {
  /**
	 * Returns the user agent
	 * @param {bool} useAlert
	 */
	inspect: function(useAlert)
	{
		if(useAlert)
			alert(navigator.userAgent);
		else
			return navigator.userAgent;
	},
	
  getVersion: function() {
    if (this.isMSIE()){
      temp=navigator.appVersion.split("MSIE");
      version=parseFloat(temp[1]);
    }
    return version;
  },
  
  /**
	 * Returns true if browser is MSIE
	 */
  isMSIE: function()
	{
		return (navigator.userAgent.toLowerCase().indexOf("msie") > -1) && !this.isOpera();
	},
	
	/**
	 * Returns true if browser is Opera
	 */
	isOpera: function()
	{
		return navigator.userAgent.toLowerCase().indexOf("opera") > -1;
	},
	
	/**
	 * Returns true if browzer is Mozilla
	 */
	isMozilla: function()
	{
		return (navigator.userAgent.toLowerCase().indexOf("mozilla") > -1) && !this.isOpera() && !this.isMSIE();
	}
}

var Log = {
  error: function(msg) {
    this.msg(msg);
  },
  
  debug: function(msg) {
    this.msg(msg);
  },
  
  msg: function(msg) {
    if (Globals.Debug)
      alert(msg);
  }
}
var FormHighlighter = {
  highlight_field: function(field) {
    Element.classNames(field).add("highlighted");
  },
  
  unhighlight_field: function(field) {
    Element.classNames(field).remove("highlighted");
  },  
  
  initialize_highlights: function(el) {
    if (!el) el = document;
    
    var fields = $A(el.getElementsByTagName("input"));
    fields = fields.concat($A(el.getElementsByTagName("textarea")));
    
    fields.each(function(el){
      if (Element.classNames(el).include("text") || el.tagName=="TEXTAREA") {
        Event.observe(el,"focus",FormHighlighter.highlight_field.bind(null,el));
        Event.observe(el,"blur",FormHighlighter.unhighlight_field.bind(null,el));
      }
    })
  }
}

// Setup the quick nav, this should execute inline and not after the page loads because we want it working immediatly
function g_initQuickNav() {
  $("project_search").setEmptyText("Quick Navigation and Search");

  var data = {
          'Companies': {type:'Company', items:current_project.companies.collect(function(company){
            return [company.id, company.name];
          })},
          'Products': {type:'Offering', items:current_project.offerings.collect(function(offering){
            return [offering.id, offering.name];
          })},
          'Customers Segments': {type:"Customer Segment", items:current_project.segments.collect(function(segment){
            return [segment.id, segment.name];
          })}
      };

  new QuickNavControl(
    'project_search',
    data,
    {choices: 10, ignoreCase: true, partialChars: 1, frequency: 0.1}
  );
}

var Layout = {
  initialize: function() {
    if (Browser.isMSIE() && Browser.getVersion() < 7.0) {
      Event.observe(window,'resize',this.constrainPage.bind(this));
      this.constrainPage(); /* contrain the page initially */
    }
  },
  
  constrainPage: function(){
    var bw = $("body_wrap");
    
    if (!bw) {
      // set a timeout and try again, body wrap wasn't loaded yet
      window.setTimeout(this.constrainPage.bind(this),20);
      return true;
    }
    
    if (document.documentElement.clientWidth < 900) {
      if (!bw.style.width) bw.style.width = "900px";
    } else {
      if (bw.style.width) bw.style.width = null;
    }
    return true;
  }
}
/* we do this right away so that the min page with works before everything is loaded */
Layout.initialize();

var Page = {

  onLoad: [],
  dynamicPage: null,
  
  // Do all page setup here so that we can apply it to dynamic and ajax pages as well
  setup: function(el, focusForms) {
    // we must use a timeout because prototype sets a 10 millisecond timeout before
    // executing scripts which could prevent some form elements from having been
    // created or more likely the onLoad handlers have not subscribed yet
    setTimeout(function() {
      Page._setup(el, focusForms);
      
      // we do this after the page has been setup to give the subscription functions
      // a chance to add things to the page
      if (el.id =="page") {
        Page.returnToScrollPosition();
      }
    }, 15);
  },
  
  
  // this is called when the main page is setup and in the header in interface.rhtml, calling
  // in both places prevents at least firefox from jumping
  returnToScrollPosition: function(){
    var hashParams = window.location.hash.sub(/#/,'').toQueryParams();
    if (hashParams.scroll) {
      window.scrollTo(0,parseInt(hashParams.scroll))
    }
  },
  
  focusFirstFormElement: function(el, form) {
    if (!form) {
      var forms = $A(el.getElementsByTagName('form')).map(Element.extend);
      form = forms[0];
    }
    if (form && !form.hasClassName('no-focus')) {
      var field = Form.findFirstElement(form);
      if (field && field.tagName.toLowerCase() != 'select' && !field.hasClassName('button')) {
        if (form.hasClassName('no-select')) {
          field.focus();
        } else {
          field.focus();
          field.select();
        }
      }
    }
  },
  
  _setup: function(el, focusForms) {
    if (typeof el == 'string') el = $(el);
    
    if (typeof focusForms == 'undefined') focusForms = true;
    
    // if browser is IE 5.5+, use onmouseover/onmouseout to to record-list hovers (since IE doesn't support hover on TR's)
    var version = '';
    if (navigator.appVersion.indexOf("MSIE")!=-1) { //Detect IE5.5+
      temp=navigator.appVersion.split("MSIE");
      version=parseFloat(temp[1]);
    }
    if (version>=5.5 && version<7) {
      var className = 'hovered';
      var rows = $$('table.record-list tr');
  
      for(var i=0, n=rows.length; i < n; ++i) {
        rows[i].onmouseover = function() {
          this.addClassName(className);
        };
        rows[i].onmouseout = function() {
          this.removeClassName(className);
        };
      }
    }
    
    // show the account agreement link in the footer if necessary
    // use window. to check if current account is defined safely
    if (window.current_account && current_account.agreement_title) {
      var link = $("account_agreement_link");
      if (link && link.childNodes.length == 0) {
        var a = Builder.node('a',{href: '/account/view_agreement'});
        a.appendChild(document.createTextNode(current_account.agreement_title));
        $("account_agreement_link").appendChild(a);
      }
    }

    // call the onload handlers
    this.onLoad.each(function(handler){handler();});
    this.onLoad = [];
    
    FormHighlighter.initialize_highlights(el);
    // focus on an element
    var forms = $A(el.getElementsByTagName('form')).map(Element.extend);
    
    if (focusForms) {
      var form = forms[0];
      this.focusFirstFormElement(el,form);
    }
  
    forms.each(function(form){
      $A(form.getElementsBySelector("input[type=submit]")).each(function(submit){
        Event.observe(submit,'click',function(submit,form){
          window.setTimeout(function(){submit.disabled = true;}, 100);// for safari
          // also show the saving icon even if this is not an ajax request
          if (form.onsubmit == '' || form.onsubmit == null) {
            activity_text = form.readAttribute('activity_text');
            if (!activity_text || activity_text != "none") {
              new ActivityIndicator(activity_text || "Saving...").show();
            }
          } else {
            // ajax form
            activity_text = form.readAttribute('activity_text');
            if (!activity_text || activity_text != "none") {
              new ActivityIndicator(activity_text).show();
            }
          }
        }.bind(this,submit,form));
      }.bind(this));
    }.bind(this));
  },
  
  // you can also just pass a page id to hide the inline content (no trigger needed)
  toggleInlineContent: function(page_id, trigger, button) {
    if (trigger) {trigger=$(trigger);}
    if (button) {button=$(button);}
    
    if (!trigger) {
      var inline_page = $(page_id);
      var container = inline_page.parentNode;
      var trigger = container.childNodes[0];
      
      var button = trigger._button;
      
      var effects = [
        new Effect.Appear(trigger, {sync: true}),
        new Effect.BlindUp(inline_page, {sync: true}),
        new Effect.Fade(inline_page, {sync: true})
      ];
      
      if (button) {
        if (!Prototype.Browser.IE) {
          effects.push(new Effect.AppearFromInvisible(button, {sync: true}));
        } else {
          button.style.visibility = 'visible';
        }
      }
      
      new Effect.Parallel(
        effects,
        {
          duration: Globals.EffectDuration,
          afterFinish: function() {
            trigger.style.position = "";
            trigger.style.zIndex = "";
            container.parentNode.insertBefore(trigger,container);
            container.parentNode.removeChild(container);
          }
        }
      );
    } else if (!trigger._animating) {
      trigger._animating = true; // prevent double clicks
      trigger._button = button;
      
      // place the trigger inside of a div that can be expanded
      // so that the tigger is absolutely positioned.
      var container = document.createElement("DIV");
      trigger.parentNode.insertBefore(container,trigger);
      trigger.parentNode.removeChild(trigger);
      container.appendChild(trigger);
      var version=0; 
      if (navigator.appVersion.indexOf("MSIE")!=-1) { //Detect IE5.5+
        temp=navigator.appVersion.split("MSIE");
        version=parseFloat(temp[1]);
      }
      if (version>=5.5 && version <7) {
        // use Element.getHeight(trigger) NOT trigger.getHeight to fix bug in ie
        container.style.height = Element.getHeight(trigger)+"px"; // IE does not use the min-height tag
      } else {
        container.style.minHeight = Element.getHeight(trigger)+"px"; 
      }
      trigger.style.position = "absolute";
      trigger.style.zIndex = 3000;
    
      new Insertion.Bottom(container,g_dynamic_pages[page_id]);
      var show_page = $(page_id);
      
      var effects = [
        new Effect.Fade(trigger, {sync: true}),
        new Effect.BlindDown(show_page, {sync: true}),
        new Effect.Appear(show_page, {sync: true})
      ];
      
      if (button) {
        if (!Prototype.Browser.IE) {
          effects.push(new Effect.FadeToInvisible(button, {sync:true}));
        } else {
          button.style.visibility = 'hidden';
        }
      }
    
      new Effect.Parallel(
        effects,
        {
          duration: Globals.EffectDuration,
          afterFinish: function() {
            Page.subscribe(function(){
              // +20 so we get a little extra room
              var viewPortHeight = document.viewport.getHeight();
              var height = Math.min(container.getDimensions().height + 40, viewPortHeight);
              var offset = viewPortHeight - (container.viewportOffset().top + height);
              if (offset > 0) offset = 0;
              new Effect.Scroll(window, { y: -offset, duration: 0.3, afterFinish: function(){ Page.focusFirstFormElement(show_page); }});
            });
            
            Page.setup(show_page, false);
            trigger._animating = false;
          }
        }
      );
    }
    
    return false;
  },
    
  toggle_content: function(pageId) {
    var primaryPage = $('page');
    
    var showPage = null;
    var hidePage = null;
    
    if (window.beforeDynamicPageLoad) {
      window.beforeDynamicPageLoad()
    }
    
    if (this.dynamicPage && this.dynamicPage.id == pageId) {
      // get rid of dynamic page and show primary page
      showPage = primaryPage;
      hidePage = this.dynamicPage;
      this.dynamicPage = null;
    } else {
      if (!this.dynamicPage) {
        // save the scroll offset before we show a dynamic page
        this.origScrollOffsets = document.viewport.getScrollOffsets();
      }
      
      // build dyanmic page
      new Insertion.Bottom(primaryPage.parentNode,g_dynamic_pages[pageId]);
      showPage = $(pageId);
      // set the appropriate hide page
      hidePage = this.dynamicPage ? this.dynamicPage : primaryPage;
      
      this.dynamicPage = showPage;
    }
    
    hidePage.hide();
    showPage.show();
    
    if (hidePage != primaryPage) {
      this._removeDynamicPage(hidePage)
    }
    
    if (showPage != primaryPage) {
      Page.setup(showPage);
      window.scrollTo(0,0);
    } else {
      // scroll back to where we were
      window.scrollTo(this.origScrollOffsets.left, this.origScrollOffsets.top)
    }
    
    setTimeout("if (window.afterDynamicPageLoad) { window.afterDynamicPageLoad(); }", 100);
    
    return false;
  },
  
  _removeDynamicPage: function(page) {
    if (Prototype.Browser.IE) {
      page.id = ""; // so we just isolate the page instead
      page.innerHTML = "";
    } else {
      page.remove(); // this causes a security warning in ie 6
    }
  },
  
  // enables event handler to be executed after a page has been prepared and shown;
  subscribe: function(handler) {
    this.onLoad.push(handler);
  },
  
  // Fades out the old content and fades in the new content. Deletes the old content.
  update_content: function(pageId,html) {
    var oldPage = $(pageId);
    
    oldPage.id = 'old_'+pageId
    var pageContainer = oldPage.parentNode;
    
    if (oldPage.hasClassName('inline-page')) {
      // This was an inline page dynamic page
      new Effect.BlindUpAndFade(oldPage,{duration: Globals.FastEffectDuration, afterFinish: function(){
        oldPage.id = ""; // so we just isolate the page instead
        oldPage.innerHTML = "";
        Page._removeDynamicPage(oldPage);
        
        // wait until after the old page is removed to insert the new page
        // so the ids don't conflict with any scripts that execute
        new Insertion.Bottom(pageContainer,html);
        var showPage = $(pageId);
        
        new Effect.BlindDownAndAppear(showPage, {duration: Globals.FastEffectDuration, afterFinish: function(){
          Page.setup(showPage);
        }})
      }})
    } else {
      // This was a full page dynamic page
      
      // remove the old page first so ids don't conflict
      this._removeDynamicPage(oldPage);
      
      new Insertion.Bottom($('page_wrapper'),html);
      var showPage = $(pageId);
      this.dynamicPage = showPage;
      
      showPage.show();
      Page.setup(showPage);
    }
  }  
}
var g_dynamic_pages = new Array();
var g_inline_help = new Array();


ActivityIndicator = Class.create();
ActivityIndicator.prototype = {
  initialize: function(message) {
    this.message = message;
    this.show();
  },
  
  show: function(){
    var indicator = $('activity_indicator');
    indicator.innerHTML = this.message || "Saving..."
    indicator.show();
  },
  
  hide: function(message) {
    var indicator = $('activity_indicator');
    indicator.hide();
  }
};


HoverMenu = Class.create();
HoverMenu.prototype = Object.extend(new Autocompleter.Base(), {
  initialize: function(trigger_element, menu_element, options) {
    this.trigger_el = $(trigger_element);
    this.menu_el = $(menu_element);

    this.options = options || {};
    
    this.options.onShow = this.options.onShow || function(element, update){ 
      if(!update.style.position || update.style.position=='absolute') {
        update.style.position = 'absolute';
        update.style.top = "-1000px";
        update.style.display ='';
        var width = update.offsetWidth;
        update.style.display ='none';
        update.style.top = "";
        

        Position.clone(element, update, {
          setHeight: false, 
          setWidth: false,
          offsetTop: element.offsetHeight,
          offsetLeft: -(width-element.offsetWidth)
        });
      }
      update.show();
      //Effect.Appear(update,{duration:0.15});
    };
    
    this.options.onHide = this.options.onHide || 
      function(element, update){ update.hide(); /*new Effect.Fade(update,{duration:0.15})*/ };
    this.options.onForceHide = this.options.onForceHide || 
        function(element, update){ update.hide(); };
        
    this.over = false;
    Event.observe(this.trigger_el,"mouseover",this.show.bind(this));
    
    Event.observe(this.trigger_el,"mouseover",this.mouseOver.bind(this));
    Event.observe(this.menu_el,"mouseover",this.mouseOver.bind(this));
    
    Event.observe(this.trigger_el,"mouseout",this.mouseOut.bind(this));
    Event.observe(this.menu_el,"mouseout",this.mouseOut.bind(this));
    
    Event.observe(this.menu_el,"click",this.forceHide.bind(this));
  },
  
  mouseOver: function() {
    this.over = true;
    clearTimeout(this.timeout);
  },
  
  mouseOut: function(){
    this.over = false;
    this.timeout = setTimeout(this.hide.bind(this),20);
  },
  
  show: function() {
    if(Element.getStyle(this.menu_el, 'display')=='none') this.options.onShow(this.trigger_el, this.menu_el);
    if(!this.iefix && 
      (navigator.appVersion.indexOf('MSIE')>0) &&
      (navigator.userAgent.indexOf('Opera')<0) &&
      (Element.getStyle(this.menu_el, 'position')=='absolute')) {
      new Insertion.After(this.menu_el, 
       '<iframe id="' + this.menu_el.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.menu_el.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
    this.trigger_el.addClassName("open");
  },
  
  fixIEOverlapping: function() {
    Position.clone(this.menu_el, this.iefix, {setTop:(!this.menu_el.style.height)});
    this.iefix.style.zIndex = 1;
    this.menu_el.style.zIndex = 2;
    Element.show(this.iefix);
  },
  
  hide: function() {
    if (this.over) return;
    if(Element.getStyle(this.menu_el, 'display')!='none') this.options.onHide(this.tigger_el, this.menu_el);
    if(this.iefix) Element.hide(this.iefix);
    this.trigger_el.removeClassName("open");
  },
  
  forceHide: function() {
    this.over = false;
    if(Element.getStyle(this.menu_el, 'display')!='none') this.options.onForceHide(this.tigger_el, this.menu_el);
    if(this.iefix) Element.hide(this.iefix);
    this.trigger_el.removeClassName("open");
  }
});

TypedAutocomplete = Class.create();
TypedAutocomplete.prototype = Object.extend(new Autocompleter.Base(), {
  initialize: function(element, items, options) {
    options.afterUpdateElement = this.itemSelected.bind(this);
    
    //this.update = Builder.node('div',{id:unique_id(), style:"display:none;", className:"quicknav"});
    // document body causes errors in ie because it may not be loaded yet so we use the header
    //$('header-top').appendChild(this.update);
    this.update = $('typed_autocomplete'); // the above works but causes the tabs to be messed up in ie. This is the best solution so far
    
    // Override on show so that it doesn't use any effects
    options.onShow = options.onShow || 
    function(element, update){ 
      if(!update.style.position || update.style.position=='absolute') {
        update.style.position = 'absolute';
        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
      }
      update.show();
    };
    options.onHide = options.onHide || 
    function(element, update){ update.hide() };
    
    this.baseInitialize(element, this.update.id, options);
    this.index = 1;
    var items = this.options.items = $H(items);
    var groups_by_type = this.groups_by_type = $H({});
    var items_by_type_and_id = this.items_by_type_and_id = $H({});
    var array = this.options.array = [];
    items.keys().each(function (group){
      groups_by_type.set(items.get(group).type,group);
      items.get(group).items.each(function(item){
        var type = items.get(group).type;
        var id = item[0];
        var name = item[1];
        if (!items_by_type_and_id.get(type)) items_by_type_and_id.set(type,$H({}));
        items_by_type_and_id.get(type).set(id,{type: type, id: id, name: name});
        array.push([group, type, item]);
      })
    });
    
    if (this.options.defaultItem) {
      this.defaultIndex = 0;
    } else {
      this.defaultIndex = 1;
    }
  },
  
  onKeyPress: function(event) {    
    if (this.active) {      
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         if (this.index > -1) {
           this.selectEntry();
           Event.stop(event);
         } else {
           this.active = false;
           return;
         }
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
         return;
      }
      
     } else {
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || 
         (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;         
     }
     
    this.changed = true;
    this.hasFocus = true;

    if (this.observer) clearTimeout(this.observer);

    this.observer = setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);

  },
  
  
  getGroupFromType: function(type) {
    return this.groups_by_type.get(type);
  },
  
  getItem: function(type,id) {
    return this.items_by_type_and_id.get(type).get(id);
  },
  
  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },
  
  markPrevious: function() {
    if(this.index > this.defaultIndex) this.index--
      else this.index = this.entryCount-1;
      
      var entry = this.getEntry(this.index);
      
      if (entry.hasClassName('category')){
        this.markPrevious();
      } else {
        //entry.scrollIntoView(false);
        // TODO: causes the browser to jump in IE 7, need to fix this
      }
  },
  
  markNext: function() {
    if(this.index < this.entryCount-1) this.index++
      else this.index = 0;
    
    var entry = this.getEntry(this.index);
    
    if (entry.hasClassName('category')){
      this.markNext();
    } else {
      //entry.scrollIntoView(false);
    }
  },
  
  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());
      
      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount = this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          if (!$(entry).hasClassName('category'))
          {
            entry.autocompleteIndex = i;
            this.addObservers(entry);
          }
        }
      } else { 
        this.entryCount = 0;
      }
      
      this.stopIndicator();
      this.index = this.options.autoHighlight ? this.defaultIndex : -1; // do not select anything initially
      
      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },
  
  selectEntry: function() {
    this.active = false;
    if (this.getCurrentEntry().readAttribute('rs:id') == 'default_item') {
      this.options.onSelectDefaultItem(this.getCurrentEntry());
    } else {
      this.updateElement(this.getCurrentEntry());
    }
  },
  
  onBlurEntry: function() {
    this.index = this.options.autoHighlight ? this.defaultIndex : -1;
    this.render();
  },
  
  addObservers: function(element) {
    Event.observe(element, "mouseout", this.onBlurEntry.bindAsEventListener(this));
    // overriden so we can use mousemove instead of mouseover
    Event.observe(element, "mousemove", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i][2][1];
          var elem_id = instance.options.array[i][2][0];
          var group = instance.options.array[i][0];
          var type = instance.options.array[i][1];
          
          var foundPos = instance.options.ignoreCase ? 
            elem.toLowerCase().indexOf(entry.toLowerCase()) : 
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0) { 
              ret.push([group,"<li rs:value='"+elem+"' rs:id='" + type + "_" + elem_id + "' rs:group='"+ group +"'><strong>" + elem.substr(0, entry.length) + "</strong>" + 
                elem.substr(entry.length) + "</li>"]);
              break;
            } else if (entry.length >= instance.options.partialChars && 
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push([group,"<li rs:value='"+elem+"' rs:id='" + type + "_" + elem_id + "' rs:group='"+ group +"'>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>"]);
                break;
              }
            }

            foundPos = instance.options.ignoreCase ? 
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : 
              elem.indexOf(entry, foundPos + 1);
          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
        
        var groups = $H({});
        ret.each(function(a){
          group = a[0];
          if (!groups.get(group)) groups.set(group,[]);
          groups.get(group).push(a[1]); 
        });
        
        var out = groups.keys().collect(function(group){ return "<li class='category'>"+group+"</li>"+groups.get(group).join('') }).join("");
        
        if (this.options.defaultItem) {
          out = "<li rs:id='default_item'>"+this.options.defaultItem(entry)+"</li>" + out;
        }
        
        return "<ul>"+out+"</ul>";
      }.bind(this)
    }, options || {});
  },
  
  itemSelected: function(element,selectedElement) {
    var typeAndId = selectedElement.readAttribute('rs:id').split("_");
    var group = selectedElement.readAttribute('rs:group');
    var value = selectedElement.readAttribute('rs:value');
    
    var type = typeAndId[0];
    var id = typeAndId[1];
    
    if (this.options.afterItemSelected)
      this.options.afterItemSelected(element,group,type,id,value);
  }
});

QuickNavControl = Class.create();
QuickNavControl.prototype = {
  initialize: function(element, items, options) {
    options.afterItemSelected = this.navigate.bind(this);
    options.autoHighlight = true;
    
    var formSubmit = $(element).form.onsubmit;
    $(element).form.onsubmit = function() {return false;}
    
    options.defaultItem = function(entry){
      return "Search for <strong>'"+entry+"'</strong>...";
    };
    options.onSelectDefaultItem = function(entry){
      var indicator = $('activity_indicator');
      indicator.innerHTML = "Searching...";
      indicator.show();
      if (formSubmit) formSubmit();
      $(element).form.submit();
      return;
    }.bind(this);
    
    Event.observe($(element),'keydown',function(event){
      if (event.keyCode == Event.KEY_RETURN && this.index > 0) {
        options.onSelectDefaultItem();
        Event.stop(event);
      }
    }.bindAsEventListener(this));
    
    this.autocomplete = new TypedAutocomplete(element, items, options);
  },
  
  navigate: function(element, group, type, id, value) {
    var indicator = $('activity_indicator');

    if (type == "Company") {
      indicator.innerHTML = "Navigating to company...";
      window.location = "/projects/"+current_project.id+"/companies/" + id;
    } else if (type == "Offering") {
      indicator.innerHTML = "Navigating to product...";
      window.location = "/projects/"+current_project.id+"/offerings/" + id;
    } else if (type == "Customer Segment") {
      indicator.innerHTML = "Navigating to customer segment...";
      window.location = "/projects/"+current_project.id+"/segments/" + id;
    }
    
    indicator.show();
  }
};

TagField = Class.create();
TagField.prototype = Object.extend(new Autocompleter.Base(), {
  initialize: function(element, button_el, relations_el, labels_el, listEl, items, options) {
    this.orig_element = element;
    var input = this.input =  $(element);
    
    this.relations_el = $(relations_el);
    this.labels_el = $(labels_el);

    this.selectedTagList = $H({});
    this.listEl = $(listEl);
    options.autoHighlight = false;
    options.afterItemSelected = this.addTag.bind(this);
    this.options = options;
    this.options['tokens'] = ',';
    this.autocomplete = new TypedAutocomplete(element, items, options);
    
    Event.observe(input,'keypress',function(event){
      if (Event.keyCode(event) == Event.KEY_RETURN && (!this.autocomplete.active || this.autocomplete.index < 0)) {
        this.add();
        Event.stop(event);
      }
    }.bindAsEventListener(this));

    // add all the selected labels
    this.labels_el.getValue().split(' ').reject(function(e){return e=='';}).each(function(label_name){
      var label_name = decodeURIComponent(label_name);
      this.addTag(null, this.autocomplete.getGroupFromType('Label'), 'Label', label_name, label_name);
    }.bind(this));
    
    // add all the selected relations
    this.relations_el.getValue().split(' ').reject(function(e){return e=='';}).each(function(relation) {
      var type_and_id = relation.split("_");
      var type = type_and_id[0];
      var id = type_and_id[1];
      var item = this.autocomplete.getItem(type,id);
      if (item) {
        this.addTag(null, this.autocomplete.getGroupFromType(item.type), item.type, item.id, item.name);
      } else {
        Log.error("Could not find item for relation: " + type + "_" + id);
      }
    }.bind(this));
    
    // add the button
    var button = $(button_el);
    Event.observe(button,'click',function(e){
      this.add();
      Event.stop(e);
    }.bind(this))
    
    if (input.form.onsubmit) {
      this.old_on_submit = input.form.onsubmit.bindAsEventListener(input.form);
      input.form.onsubmit = function(){return false;}; // must be false to prevent ajax based forms from submitting
    } else {
      this.old_on_submit = function(){return true;};
    }
    
    Event.observe(input.form,'submit',function(e) {
      this.transferSelectedItems(e,false);
      return this.old_on_submit(e);
    }.bindAsEventListener(this));
  },
  
  findItem: function(value) {
    var instance = this.autocomplete;
    for (var i = 0; i < instance.options.array.length; i++) { 
      var elem = instance.options.array[i][2][1];
      
      var found = elem.toLowerCase() == value.toLowerCase();
      if (found) {
        var elem_id = instance.options.array[i][2][0];
        var group = instance.options.array[i][0];
        var type = instance.options.array[i][1];
        return { group: group, type: type, id: elem_id, name: elem };
      }
    }
  },

  _build_token: function(tokens,start,end) {
    var buff = []
    for (var i = start; i <= end; i++) {
      buff.push(tokens[i]);
    }
    return buff.join(",");
  },
  
  // this is called if the auto complete is not used
  add: function() {
    var input = this.input;
    if (input.getValue() && input.getValue() != '') {
      var value = input.getValue();
      
      // try all possible combinations of tokens because some entity names might have a comma!
      var tokens = value.split(",").reject(function(t){return !t || t.strip() == ""});
      for (var i = 0; i < tokens.length; i++) {
        for (var j = tokens.length-1; j >= i; j--) {
          var token = this._build_token(tokens,i,j).strip();
          var item = this.findItem(token);
          if (item) {
            this.addTag(input, item.group, item.type, item.id, item.name);
            i = j;
            break;
          } else if (i == j) {
            this.addTag(input, 'Labels', 'Label', token, token);
          }
        }
      }
    }
    this.input.value = "";
    this.autocomplete.hide();
    this.input.focus();
  },
  
  transferSelectedItems: function(e) {
    // first check if the field has any more tags that the user might have forgot to add
    var extra_input = this.input.getValue();
    if (extra_input && extra_input.strip() != ''){
      this.add();
    }
  
    // must! clear these in case nothing is added
    this.labels_el.value = '';
    this.relations_el.value = '';
    
    var relations = [];
      
    this.selectedTagList.keys().each(function(type){
      var list = this.selectedTagList.get(type);
      if (list) {
        var items = this.selectedTagList.get(type).items;
        if (type == "Label"){
          this.labels_el.value = items.collect(function(item){
            return encodeURIComponent(item.id);
          }).join(" ");
        } else {
          this.relations_el.value += items.collect(function(item){
            relations.push(item.type+"_"+item.id);
          }).join(" ");
        }
      }
    }.bind(this));
    
    this.relations_el.value = relations.join(" ");
  },
  
  findOrCreateSelectedTagList: function(type) {
    var group = this.autocomplete.getGroupFromType(type);
    if (this.selectedTagList.get(type)) {
      return this.selectedTagList.get(type);
    } else {
      var list = this.selectedTagList.set(type, {element: Builder.node("li"), group: group, type: type, items: $A([])});
      this.listEl.appendChild(list.element);
      return list;
    }
  },
  
  renderSelectedTagList: function(list) {
    list.element.innerHTML = '';
    var b = Builder.node('b',list.group + " ");
    list.element.appendChild(b);
    var span = Builder.node('span', {'class': 'time-ago'}, "(" + list.items.length + ")");
    b.appendChild(span);
    
    var ul = Builder.node("ul");
    list.element.appendChild(ul);

    list.items.each(function(item){
      var li = Builder.node('li',{'class': item.type + "_tag"}, item.value);
      ul.appendChild(li);
      Event.observe(li, "mousemove", function(){ li.addClassName('remove'); }.bind(this));
      Event.observe(li, "mouseout", function(){ li.removeClassName('remove'); }.bind(this));
      Event.observe(li, "click", function(){
        list.items = list.items.without(item);
        if (list.items.length == 0) {
          list.element.remove();
          this.selectedTagList.set(list.type, null);
        } else {
          this.renderSelectedTagList(list);
        }
      }.bind(this));
    }.bind(this));
  },
  
  addTag: function(element, group, type, id, value) {
    if (element) {
      element.value = element.value.gsub(value,'');
    }
    var list = this.findOrCreateSelectedTagList(type);
    var item = {type: type, id: id+'', value: value};
    var existing_item = list.items.find(function(s){
      return (s.type == item.type && (s.id+'').toLowerCase() == item.id.toLowerCase())
    });
    
    if (!existing_item) {
      list.items.push(item);
      this.renderSelectedTagList(list);
    } else {
      alert("That tag has already been added.");
    }
  },
  
  reset: function(new_data) {    
    // reset currently selected tags
    this.selectedTagList = $H({});
    
    // remove all of the current selected tags from the page, skip the directions tag
    var lis = this.listEl.getElementsBySelector("li");
    for (var i = 1; i < lis.length; i++) {
      lis[i].remove();
    }
    
    var new_input = new Element('input', {'type': 'text', 'class': 'text about', 'autocomplete': 'off'});
    this.input.parentNode.insertBefore(new_input, this.input);
    this.input.remove();
    this.input = new_input;
    
    // reset the autocomplete with the data from the currently selected project
    this.autocomplete = new TypedAutocomplete(new_input, new_data, this.options);
  }
  
});

var ExpandingTextArea = Class.create();
ExpandingTextArea.prototype = {
  initialize: function(textarea_id) {
    this.textarea = $(textarea_id);
    Event.observe(this.textarea, 'focus', function(){
      this.textarea.addClassName('expanded');
      this.textarea.autoExpand();
    }.bind(this));
    Event.observe(this.textarea, 'blur', function(){
      if (this.textarea.getValue() == '') {
        this.textarea.removeClassName('expanded');
        this.textarea.setStyle({height:''}); // reset auto expand height
      }
    }.bind(this));
  }
}

var EditInPlaceControl = Class.create();
EditInPlaceControl.prototype = {
  initialize: function(el, options) {
    this.options = {
      textarea: false,
      width: "95%",
      onChange: function(value){},
      filterValue: function(value){return value.strip();}
    }
    Object.extend(this.options,options || {});
    
    
    var el = $(el);
    var span = this.span = new Element('span');
    span.innerHTML = el.innerHTML;
    el.innerHTML = '';
    el.appendChild(span);
    
    this.el = $(el);
    
    Event.observe(this.span, 'mouseover', this.mouseOver.bind(this));
    Event.observe(this.span, 'mouseout', this.mouseOut.bind(this));
    Event.observe(this.span, 'click', this.click.bind(this));
  },
  
  mouseOver: function(){
    this.span.addClassName('edit-in-place-over');
  },
  
  mouseOut: function() {
    this.span.removeClassName('edit-in-place-over');
  },
  
  click: function(){
    var span = this.span;
    var tag = this.options.textarea ? 'textarea' : 'input';
    
    var input = new Element(tag,{'class':'text'});

    var spanHeight = this.span.getHeight();
    input.setStyle({width: this.options.width, height: spanHeight+"px"});
    
    var styles={};
    ['lineHeight', 
     'fontSize', 
     'fontFamily'].each(function(style){
      styles[style] = span.getStyle(style);
    });
    input.setStyle(styles);
    
    input.setValue(this.span.innerHTML.unescapeHTML());
    this.span.parentNode.insertBefore(input,this.span);
    this.span.hide();
    
    if (this.options.textarea)
      input.autoExpand();
    
    input.focus();
    input.select();
    
    Event.observe(input, 'blur', this.transferContents.bind(this, input));
    
    Event.observe(input,'keypress',function(event){
      if (Event.keyCode(event) == Event.KEY_RETURN) {
        this.transferContents(input);
        Event.stop(event);
      } else if (Event.keyCode(event) == Event.KEY_ESC) {
        input.remove();
        span.show();
      }
    }.bindAsEventListener(this));
  },
  
  transferContents: function(input) {
    var value = this.options.filterValue(input.getValue());
    this.span.innerHTML = "";
    this.span.appendChild(document.createTextNode(value));
    input.remove();
    this.span.show();
    this.options.onChange(value);
  }
};

var ConcernControl = Class.create();
ConcernControl.prototype = {
  initialize:function(trigger,container, fieldName, concernLevel){
    this.trigger = $(trigger);
    this.container = $(container);
    
    this.container.appendChild(new Element("b").update("Concern Level"));
    var removeLink = new Element('a', {'class': 'remove-link', href: '#'}).update("(Remove)");
    removeLink.onclick = function() { return false; };
    this.container.appendChild(removeLink);
    
    var sliderContainer = new Element("div");
    this.container.appendChild(sliderContainer);
    
    var showStepSlider = function() {
      this.trigger.hide();
      this.container.show();
      var values= ['Potential', 'Low', 'Medium', 'High'];
      var value = values[concernLevel > 0 ? concernLevel-1 : 0 ];
      new StepSlider(sliderContainer,values,fieldName, value);
    }.bind(this);
    
    this.trigger.onclick = function() { return false; };
    Event.observe(this.trigger, 'click', showStepSlider);
    
    Event.observe(removeLink, 'click', function() {
      sliderContainer.innerHTML = "";
      this.container.hide();
      this.trigger.show();
      
      // add a hidden field for the concern level to make it 0
      sliderContainer.appendChild(new Element('input', {'name': fieldName, 'type': 'hidden', 'value': '0'}));
    }.bind(this));
    
    if (concernLevel > 0) {
      showStepSlider();
    }
  }
}

var ClippingControl = Class.create();
ClippingControl.linkDetailPath = null; // set in page
ClippingControl.prototype = {
  clippingsCount: 0,
  
  initialize: function(trigger, el, paramName, clippings, linkableType, linkableId, postTitle) {
    var trigger = this.trigger = $(trigger);
    var el = this.el = $(el);
    this.paramName = paramName;
		this.linkableType = linkableType;
    this.linkableId = linkableId;
    this.postTitle = $(postTitle);
    
    // add the attach header
    var header = this.header = new Element('div',{'class':'attach-header'}).update("Link:");
    el.appendChild(header);
    
    // prep the trigger so it can receive the events
    trigger.onclick = function() { return false; };
    Event.observe(trigger, 'click', this.promptForUrl.bind(this));
    
    this.initializing = true;
    
    // create the initial clippings
    clippings.each(function(link){
      this.addClipping(link);
    }.bind(this));
    
    this.initializing = false;
  },
  
  addEmptyClippings:function(){
    this.emptyClippingField = new Element('input',{type:'hidden',value:'',name:this.paramName+'[links_attributes]'});
    this.el.appendChild(this.emptyClippingField);
  },
  
  removeEmptyClippings:function(){
    if (this.emptyClippingField) {
      this.emptyClippingField.remove();
      this.emptyClippingField = null;
    }
  },
  
  addClipping: function(link, defaultLinkDetail){    
    this.el.show();
    
    // must remove to prevent parameter parsing error in bookmarklet
    this.removeEmptyClippings();
    
    this.clippingsCount++;
    
    if (defaultLinkDetail) {
      link.title = link.title || defaultLinkDetail.title;
      link.description = link.description || defaultLinkDetail.description;
    }
    
    var id = link.id || null;
    var title = link.title || link.url;
    var description = link.description;
    var url = link.url;
    var count = link.count;
    
    var field = new Element('div', {'class': 'field'});
    this.el.appendChild(field);
    
    var preview = new Element('div',{'class':'link-preview'});
    field.appendChild(preview);
    
    var inputId = new Element("input",{type:'hidden', value: id, name: this.paramName+'[links_attributes][][id]'});
    var inputTitle = new Element("input",{type:'hidden', value: title, name: this.paramName+'[links_attributes][][title]'});
    var inputDescription = new Element("input",{type:'hidden', value: description, name: this.paramName+'[links_attributes][][description]'});
    var inputUrl = new Element("input",{type:'hidden', value: url, name: this.paramName+'[links_attributes][][url]'});
    preview.appendChild(inputId);
    preview.appendChild(inputTitle);
    preview.appendChild(inputDescription);
    preview.appendChild(inputUrl);
    
    
    var remove = new Element('a', {href: '#', 'class': 'remove-link'}).update('Remove');
    remove.onclick = function() { return false; }
    remove.setStyle({'float': "right"});
    preview.appendChild(remove);
    Event.observe(remove, 'click', function() {
      this.clippingsCount--;
      var target = field;
      if (this.clippingsCount == 0) {
        this.trigger.update("Share link");
        target = this.el;
        this.addEmptyClippings();
      }
      new Effect.BlindUpAndFade(target, {duration: 0.2, afterFinish: function(){
        field.remove();
        }});
    }.bind(this))
    
    if (title) {
      var titleEl = new Element('div',{'class':'title'}).update(title.escapeHTML());
      preview.appendChild(titleEl);
      new EditInPlaceControl(titleEl, {
        maxLength:255, 
        width: "80%", 
        onChange: function(value){ inputTitle.value= value; },
        filterValue: function(value) { 
          return (value.strip() == "") ? url : value.strip(); 
        }
      });
    }
    
    if (description){
      var descriptionEl = new Element('div', {'class': 'description'}).update(description.escapeHTML());
      preview.appendChild(descriptionEl);
      new EditInPlaceControl(descriptionEl, {textarea:true, onChange: function(value){
        inputDescription.value=value;
      } });
    }
    
    if (url) {
      var urlEl = new Element('div', {'class': 'url'}).update(url.escapeHTML());
      preview.appendChild(urlEl);
    }
    
    // has this link been posted in this project yet?
    if (count > 0) {
      var postCount = new Element('div', {'class' : 'post-count'})
      var postCountSpan = new Element('span', {'class' : 'highlight'});
      postCountSpan.appendChild(document.createTextNode("This link has been posted "+ count + " other time"+(count > 1 ? "s" : "")  + " "));
      var postCountLink = new Element('a', {'class':'action-link', 'href':'#'}).update("(show)");
      var placeholder = new Element('div');
      var showing = false
      
      Event.observe(postCountLink,'click', function() {
        if(!showing) {
          var container = new Element('div', {'class': 'related-posts'}).update("Loading...");
          placeholder.appendChild(container);
          postCountLink.update("(hide)");
          
          new Ajax.Request(ClippingControl.linkDetailPath, {
              method: 'get',
              parameters: {url: url, show_related: true},
              onSuccess: function(transport){
                if (showing){
                  container.update("");
                  new Records.List(container,transport.responseJSON);
                }
              }.bind(this)
            }); 
        } else {
          placeholder.update("");
          postCountLink.update("(show)");
        }
        
        showing = !showing;
      });
      
      preview.appendChild(postCount);
      postCount.appendChild(postCountSpan);
      postCountSpan.appendChild(postCountLink);
      preview.appendChild(placeholder);
    }
    
    if (this.clippingsCount == 1) {
      this.trigger.update("Share another link");
      var postTitle = this.postTitle;
      if (!this.initializing && postTitle && postTitle.getValue() == '' && link.title && link.title != '')
      {
        postTitle.setValue(link.title);
        postTitle.removeClassName('blank');
        postTitle.focus();
        postTitle.select();
      }
      this.header.update("Link:");
    } else {
      this.header.update("Links:");
    }
  },
  
  addClippingFromUrl: function(url){
    var urlDetail = null;
    
    if (!Object.isString(url)) {
      var urlDetail = url
      url = url.url
    }
    
    new Ajax.Request(ClippingControl.linkDetailPath, {
        method: 'get',
        parameters: {url: url, linkable_type: this.linkableType, linkable_id: this.linkableId},
        onSuccess: function(transport){
          var serverUrlDetail = transport.responseJSON;
          this.addClipping(serverUrlDetail, urlDetail);
        }.bind(this),
        onFailure: function(transport){
          alert("The service is experiencing temporary issues. Please try your request again in a few moments.");
        }
      });
  },
  
  promptForUrl:function(){
    // create the body and prompt
    var body = new Element('div');
    body.appendChild(new Element('div').update("Link address:<br/>"));
    var input = new Element('input',{type:'text', id:'clipping_address', 'class':'text wide'});
    body.appendChild(input);
    body.appendChild(new Element('br'));
    body.appendChild(new Element('br'));
    
    // create the buttons
    var buttons = new Element('div',{'class':'buttons'});
    var button = new Element('button',{'class':'button'}).update("Continue");
    var cancelLink = new Element('a', {href: '#', 'class': 'cancel-link'}).update("Cancel");
    body.appendChild(buttons);
    buttons.appendChild(button);
    buttons.appendChild(document.createTextNode(' or '));
    buttons.appendChild(cancelLink);
    
    var submitObserver = function (){
      var url = input.value;
      if (url && url.strip() != '') {
        this.addClippingFromUrl(input.value);
        deactivate_lightbox();
      } else {
        alert('You must enter the link address');
      }
    }.bind(this);
    
    // link up the buttons to events
    cancelLink.onclick = function() { return false; };
    Event.observe(cancelLink, 'click', deactivate_lightbox.bind());
    Event.observe(button, 'click', submitObserver);
    
    // link up enter too
    Event.observe(input,'keypress',function(event){
      if (Event.keyCode(event) == Event.KEY_RETURN) {
        submitObserver();
        Event.stop(event);
      }
    }.bindAsEventListener(this));
    
    new lightbox({header:'Share Link', body: body}, 
                {height: 145});
    var funct = function (){
      $("clipping_address").focus();
    };
    funct.delay(0.2);
  }
}

var AttachmentControl = Class.create();
AttachmentControl.upgradeMessage = "<div class='upgrade-message'><b>You can not attach files with the free plan.</b>Your account owner needs to upgrade to a higher plan to allow attachments.</div>"
AttachmentControl.prototype = {
  attachmentCount: 0,
  pendingAttachmentCount: 0,
  
  initialize: function(trigger, el, paramName, temp_attachments, existing_attachments, postTitle) {    
    var trigger = this.trigger = $(trigger);
    var el = this.el = $(el);
    this.paramName = paramName;
    this.postTitle = $(postTitle);
    
    // prep the trigger so it can receive the events
    trigger.onclick = function() { return false; };
    
    // create a container for the attachments
    var div = Builder.node("div");
    div.className="attachment-control";
    this.el.appendChild(div);
    this.container = div;

    this.newAttachmentLinkWrapper = new Element('div', {'class':'attachment-link'});
    this.container.appendChild(this.newAttachmentLinkWrapper);

    var newAttachmentLink = Builder.node("a");
    this.newAttachmentLink = newAttachmentLink;
    this.newAttachmentLinkWrapper.appendChild(newAttachmentLink);
    newAttachmentLink.className = 'attachment-link';
    newAttachmentLink.href = '#';
    newAttachmentLink.onclick = function() { return false; };
    newAttachmentLink.appendChild(document.createTextNode("Attach another file"));
    newAttachmentLink.hide(); // we aren't using this anymore but I may use it again later
    
    if (!current_account.freePlan()) {
      // find the parent form
      this.form = this.el.ancestors().find(function(el){
       return el.tagName == "FORM"; 
      });
    
      Event.observe(this.form,'submit',function(){
        if (this.pendingAttachmentCount > 0){
          // replace the attach files link with a loading icon
          var loading = Builder.node('div');
          loading.className="uploading";
          loading.update('Uploading file'+(this.attachmentCount > 1 ? 's' :'')+', please wait...');
          this.newAttachmentLinkWrapper.insertBefore(loading,newAttachmentLink);
          newAttachmentLink.remove();
        }
      }.bind(this));

      // add the existing attachments
      if (existing_attachments && existing_attachments.length > 0) {
        existing_attachments.each(function(attachment){
          this._add_temp_attachment(attachment, false /*do not add a field for it*/);
        }.bind(this));
        
        this._showAttachments();
      }
    
      // add the temp attachments
      if (temp_attachments && temp_attachments.length > 0) {
        temp_attachments.each(function(attachment){
          this._add_temp_attachment(attachment, true /* add a field for it */);
        }.bind(this));
        
        this._showAttachments();
      }
    
      Event.observe(newAttachmentLink,'click',this._new_attachment.bind(this));
      Event.observe(trigger,'click',function(){
        this._showAttachments();
        this._new_attachment.bind(this).delay(0.1);
      }.bind(this));
    } else {
      Event.observe(trigger,'click',function(){
        this.trigger.hide();
        this.container.parentNode.show();
        this._upgrade_message();
      }.bind(this));
    }
  },
  
  _showAttachments: function() {
    this.trigger.update('Attach another file');
    this.container.parentNode.show();
  },
  
  _extension: function(filename){
    if (!filename) return "";
    
    var match = filename.match(/\.([^\.]+)$/)
    if (match)
      return match[1].toLowerCase();
    else
      return "";
  },
  
  _add_temp_attachment: function(attachment,add_field) {
    // increment the attachment count
    this.attachmentCount++;
    
    var div = Builder.node('div');
    div.className = 'temp-attachment attachment';
    this.container.insertBefore(div,this.newAttachmentLinkWrapper);
    
    // add a table to wrap the new attachment
    var table = Builder.node('table');
    table.className = 'temp-attachment attachment';
    div.appendChild(table);
    // add a table body to the table
    var tbody = Builder.node('tbody');
    table.appendChild(tbody);
    // add a tr to the table
    var tr = Builder.node('tr');
    tbody.appendChild(tr);
    var td1 = Builder.node('td');
    tr.appendChild(td1);
    var td2 = Builder.node('td');
    tr.appendChild(td2);
    
    // add a hidden field so that the temp attachment id is sent back to the server
    if (add_field) {
      var input = Builder.node('input');
      input.type = "hidden";
      input.name = this.paramName + "[attachment_data][temp_attachment_ids][]";
      input.value = attachment.id;
      td1.appendChild(input);
    }
    // add a div with the name of the attachment
    var span = Builder.node('div');
    span.className='attachment-name';
    span.addClassName('file-'+this._extension(attachment.filename));
    
    if (attachment.error_messages && attachment.error_messages != '') {
      span.addClassName('failed');
    }
    td1.appendChild(span);
    span.appendChild(document.createTextNode(attachment.filename));
    
    // add a remove link
    var removeLink = Builder.node('a');
    td2.appendChild(removeLink);
    removeLink.className = "remove-link";
    removeLink.href = '#';
    removeLink.onclick = function() { return false; };
    removeLink.appendChild(document.createTextNode('(Remove)'));
    Event.observe(removeLink,'click',this._remove_attachment.bind(this,div,attachment));
    
    // add any error messages
    if (attachment.error_messages && attachment.error_messages != '') {
      var errors = Builder.node('div');
      errors.className = "formError";
      td1.appendChild(errors);
      errors.appendChild(document.createTextNode(attachment.error_messages));
    }
  },
  
  _upgrade_message: function() {
    this.el.innerHTML = AttachmentControl.upgradeMessage;
    this.el.hide();
    Effect.BlindDownAndAppear(this.el,{duration:.2});
  },
  
  // for the default attachment
  addNewAttachment: function(fileField){
    // increment the attachment count
    this.attachmentCount++;
      
    var pending = false;
    
    var div = Builder.node('div');
    div.style.display = "none";
    div.className = 'new-attachment attachment';
    this.container.insertBefore(div,this.newAttachmentLinkWrapper);
    
    // add a table to wrap the new attachment
    var table = Builder.node('table');
    table.className = 'new-attachment attachment';
    div.appendChild(table);
    // add a table body to the table
    var tbody = Builder.node('tbody');
    table.appendChild(tbody);
    // add a tr to the table
    var tr = Builder.node('tr');
    tbody.appendChild(tr);
    var td1 = Builder.node('td');
    tr.appendChild(td1);
    var td2 = Builder.node('td');
    tr.appendChild(td2);
    
    // add the file upload control
    var file = null;
    
    if (fileField) {
      file = fileField;
      file.name = this.paramName + "[attachment_data][attachments][]";
    } else {
      var file = Builder.node('input', {type: 'file'})
      file.className = "file";
      file.name = this.paramName + "[attachment_data][attachments][]";
      file.size = 15;
    }
    td1.appendChild(file);
    
    // add a remove link
    var removeLink = Builder.node('a');
    td2.appendChild(removeLink);
    removeLink.href = '#';
    removeLink.onclick = function() { return false; };
    removeLink.className = 'remove-link';
    removeLink.appendChild(document.createTextNode('Remove'));
    Event.observe(removeLink,'click',function(div){
      if (pending) this.pendingAttachmentCount--;
      this._remove_attachment(div,null);
    }.bind(this,div));
    
    var onReceiveFile = function(){
      file.style.position="absolute";
      file.style.top = "-2000px" // move it off screeen
      //file.style.visibility = "hidden"; // this won't work with non ajax based submissions
      // so set the opacity and position instead and also make it really small so that no one can click it to be safe
      file.setStyle({opacity: 0.0});
      file.setStyle({width:"1px",height:"1px"});
      
      // add a div with the name of the file
      var span = Builder.node('div');
      span.className='attachment-name';
      td1.insertBefore(span,file);
      var filename = file.getValue().split("/");
      filename = filename[filename.length-1].split("\\")
      filename = filename[filename.length-1];
      span.addClassName('file-'+this._extension(filename));
      span.appendChild(document.createTextNode(filename));
      div.removeClassName('new-attachment');
      div.addClassName('temp-attachment');
      table.removeClassName('new-attachment');
      table.addClassName('temp-attachment');
      removeLink.innerHTML = "(Remove)";
      
      // increment the pending attachment count
      this.pendingAttachmentCount++;
      pending = true;
      
      // set the default title
      var postTitle = this.postTitle;
      if (postTitle && postTitle.getValue() == '') {
        postTitle.setValue(filename.gsub(/\.[^\.]+$/,"").gsub(/(?:[\_\-\.])/," "));
        postTitle.removeClassName('blank');
        postTitle.focus();
        postTitle.select();
      }
    }.bind(this);
    
    if (fileField) {
      onReceiveFile();
      this._showAttachments();
    } else {
      Event.observe(file,'change',onReceiveFile);
    }
    
    // change the text of the newAttachmentLink if this is the first attachment
    if (this.attachmentCount == 1) {
      this.newAttachmentLink.innerHTML = "Attach another file";
    }
    
    Effect.BlindDownAndAppear(div,{duration:.2, afterFinish: function(){
      // focus on the file field
      if (!fileField) file.focus();
    }});
  },
  
  _new_attachment: function() {
    this.addNewAttachment();
  },
  
  _remove_attachment: function(div, attachment) {
    // only confirm the deletion if the attachment is present and is not temporary
    if (attachment && !attachment.temporary && !confirm('Are you sure you want to delete this attachment')) {
      return false;
    }
    
    // decrement the attachment count
    this.attachmentCount--;
    
    var fadeTarget = div;
    // change the text of the newAttachmentLink if this was the last attachment
    if (this.attachmentCount == 0) {
      fadeTarget = this.container.parentNode;
      this.trigger.update("Attach file");
    }
    
    Effect.BlindUpAndFade(fadeTarget, {duration:.2, afterFinish: function(){
      // focus on the file field
      div.remove(); 
    }});
    
    // send a request to the server to delete the attachment
    if (attachment && attachment.id && attachment.id != '') {
      new Ajax.Request('/attachments/'+attachment.id, {
        method: 'delete',
        onFailure: function(transport) {
          transport.responseJSON.each(function(error){
            alert(error[0] + ' ' + error[1]);
          });
        }.bind(this)
      });
    }
  }
}

var Account = Class.create();
Account.prototype = {
  subdomain: null,
  plan: null,
  
  initialize: function(options) {
    this.subdomain = options.subdomain;
    this.plan = options.plan;
    this.showCustomers = options.showCustomers;
  },
  
  freePlan: function()
  {
    return this.plan == 1;
  }
}

var Project = Class.create();
Project.prototype = {
  id: null,
  companies: null,
  offerings: null,
  segments: null,
  
  records_by_key: null,
  
  initialize: function(id) {
    this.id = id;
  },
  
  name_of: function(type,id) {
    var record = this.find_record(type,id);
    return record ? record.name : null;
  },
  
  find_record: function(type,id) {
    if (!this.records_by_key) this._index_records_by_id();
    var key = id ? type+"_"+id : type; // allow one or two parameter based queries
    var record = this.records_by_key[key];
    return record;
  },
  
  find_record_by_sid: function(sid) {
    if (!this.records_by_sid) this._index_records_by_sid();
    return this.records_by_sid[sid];
  },
  
  _index_records_by_id: function() {
    this.records_by_key = {};
    this._index_record_type_by_id('Company',this.companies);
    this._index_record_type_by_id('Offering',this.offerings);
    this._index_record_type_by_id('Segment',this.segments);
  },
  
  _index_record_type_by_id: function(type,records) {
    var count = records.length;
    for (var i = 0; i < count; i++) {
      var record = records[i];
      this.records_by_key[type+"_"+record.id] = record;
    }
  },
  
  _index_records_by_sid: function() {
    this.records_by_sid = {};
    [{records:this.companies, rtype: 'Company'},{records:this.offerings, rtype:'Offering'},{records:this.segments, rtype:'Segment'}].each(function(record_type){
      var records = record_type.records;
      var type = record_type.rtype;
      var count = records.length;
      for (var i = 0; i < count; i++) {
        var record = records[i];
        this.records_by_sid[record.sid] = record;
        record.type = type;
      }
    }.bind(this));
  }
}

var StepSlider = Class.create();
StepSlider.prototype = {
  values: null,
  el: null,
  name: null,
  
  initialize: function(el_id, values, name, value, options) {
    this.options = {
      showStatus: true,
      onChange: function(){},
      onSlideChange: function(){}
    }
    Object.extend(this.options,options || {});
    
    this.el = $(el_id);
    this.values = values;
    this.name = name;
    this._createSlider(value);
  },
  
  _createSlider: function(value) {
    
    var el = this.el;
    var values = this.values;
    var name = this.name;
    
    // create the hidden field to store the value
    // or find the existing one
    var hidden = null;
    if ($(name)) {
      hidden = $(name);
      value = $F(name);
    } else {
      hidden = document.createElement("INPUT");
      hidden.type = "hidden";
      hidden.name = this.name;
      // get the index of value
      for (var i = 0; i < this.values.length; i++) {
        if (this.values[i] == value) {
          hidden.value = i+1;
          break;
        }
      }
      el.appendChild(hidden);
    }

    // create the slider and its parts
    var slider = document.createElement("DIV");
    slider.className = value ? "slider" : "slider_inactive";
    el.appendChild(slider);

    var slider_left = new Element('div');
    slider_left.className = "slider_left";
    slider.appendChild(slider_left);
		
    var slider_handle = new Element('div');
    slider_handle.className = "slider_handle";
    slider.appendChild(slider_handle);
		
    // create the status td
	  var status_container = new Element('div');
	  status_container.className="slider-status";
	  if (this.options.showStatus == false) status_container.hide();
	  el.appendChild(status_container);
		
		var status = new Element('div');
		status.className="slider-unset";
		status_container.appendChild(status);
    		
		
		// now add the slider element
		var sliderLimited = new Control.Slider(slider_handle, slider, {range:$R(1,values.length)});
			
		var onSlideChange = this.options.onSlideChange;
		var lastValue = null;
		
		sliderLimited.options.onSlide = function(value) {
		  slider.className = "slider";
		  for (var i = 1; i <= values.length; i++) {
		    if (value < (i+.5)) {
		      if (lastValue != i) {
		        lastValue = i;
		        status.innerHTML = values[i-1];
		        status.className = "slider-"+i;
		        onSlideChange(i, values[i-1]);
	        }
          break;
		    }
		  }
    };
    
    // set the initial value
		sliderLimited.setValue(hidden.value);
    sliderLimited.options.onSlide(hidden.value); // ensure the class it set properly
    status.innerHTML = values[hidden.value-1];
    
    // make it snap to the correct values at the end of the slide
    sliderLimited.options.onChange = function(value) {
      if (value.toFixed() != value) {
        var nvalue = null;
        for (var i = 0; i < values.length; i++) {
          if (value < i + 0.5) {
            nvalue = i;
            break;
          }
        }
        
        if (nvalue) {
          sliderLimited.setValue(nvalue);
          value = nvalue;
        } else {
          sliderLimited.setValue(values.length);
          value = values.length;
        }
      }
    
      hidden.value = value;
      sliderLimited.options.onSlide(value);
      this.options.onChange(value);
    }.bind(this);
  }
  
}

var StarControl = Class.create();
StarControl.prototype = {
  initialize: function(a_element,visitable_type,visitable_id) {
    this.element = $(a_element);
    this.count_element = $(a_element+"_count");
    this.visitableType = visitable_type.toLowerCase();
    this.visitableId = visitable_id;
    Event.observe(this.element, 'click', this.changeStar.bindAsEventListener(this));
  },
  
  changeStar: function(event) {
    // get the current state
    var starred = this.element.hasClassName('starred-by-user')
    var method = null;
    var parameters = {};
    var extra_url = '';

    parameters[this.visitableType+"_id"] = this.visitableId;
    
    var star_this_li = $('star_this_li');
    
    var old_count = parseInt(this.count_element.innerHTML);
    
    if (starred) {
      method = 'delete'
      this.element.removeClassName('starred-by-user');
      extra_url = "/1"; // this isn't actually used but enables routes to work properly
      if (star_this_li) star_this_li.show();
      this.count_element.innerHTML = old_count-1;
    } else {
      method = 'post';
      this.element.addClassName('starred-by-user'); 
      if (star_this_li) star_this_li.hide();
      this.count_element.innerHTML = old_count+1;
    }
    
    var url = "/projects/"+current_project.id+"/stars"+extra_url;
    
    new Ajax.Request(url, {
      method: method,
      parameters: parameters,
      onFailure: function(transport) {
        transport.responseJSON.each(function(error){
          alert(error[0] + ' ' + error[1]);
        });
      }.bind(this)
    });
  }
}

var RatingSelector = Class.create();
g_rating_selectors = {};
RatingSelector.getSelectors = function(name){
  return g_rating_selectors[name];
};
RatingSelector.prototype = {
	initialize: function(element,type,value,options) {
	    this.options = Object.extend({
	       onRate: function(rating) {
	       },
	       disabled: false
	    },options || {});
	  this.el = $(element);
		this.lis = [];
		var value = this.value = Math.round(value);
		var div = Builder.node("div");
		div.addClassName('rating');
		if (!this.options.disabled) div.addClassName('editable');
		div.addClassName(type);
		ul = document.createElement("ul");
		div.appendChild(ul);
		this.event_bindings = [];
		for (var i = 0; i < 5; i++)
		{
			li = Builder.node("li", {className: 'no-value'});
			if (!this.options.disabled) {
			  this.event_bindings[i] = {};
			  this.event_bindings[i]['mouseover'] = this._select_rating.bind(this,i+1);
			  this.event_bindings[i]['mouseout'] = function(){this._select_rating(this.value)}.bind(this);// need to do it with closure
			  this.event_bindings[i]['click'] = this.setValue.bind(this,i+1);
			  li.title = "Rate this " + (i+1) + " out of 5";
			  this._observe_li(li,i);
		  }
			ul.appendChild(li);
			this.lis.push(li);
		}
		this._select_rating(value);		
		
    var clearDiv = Builder.node("div");
    clearDiv.className = "cd";
    div.appendChild(clearDiv);
		
		this.el.parentNode.insertBefore(div,this.el);
	},
	
	_observe_li: function(li,i) {
	  Event.observe(li,"mouseover",this.event_bindings[i]['mouseover']);
	  Event.observe(li,"mouseout",this.event_bindings[i]['mouseout']); // need to do it with closure
	  Event.observe(li,"click",this.event_bindings[i]['click']);
	},
	
	_unobserve_li: function(li,i) {
	  Event.stopObserving(li,"mouseover",this.event_bindings[i]['mouseover']);
	  Event.stopObserving(li,"mouseout",this.event_bindings[i]['mouseout']); // need to do it with closure
	  Event.stopObserving(li,"click",this.event_bindings[i]['click']);
	},
	
	/* This method only temporarilly disabled voting, it is different than the disable option to the constructor */
	disableEvents: function(yes){
	  var funct;
	  if (yes)
	    funct = this._unobserve_li.bind(this);
	  else
	    funct = this._observe_li.bind(this);
    
    var i = 0;
	  this.lis.each(function(li){ funct(li,i++); });
	},
	
	/* Register this rating selector so that it can be associated with an array */
	register: function(name) {
	  if (!g_rating_selectors[name])
	    g_rating_selectors[name] = []
	  
	  g_rating_selectors[name].push(this);
	},
	
	updateRating: function(rating,ratings_count){
	   // ratings_count used to be updated but now I display "rating saved!" instead
	   this.field.value = rating;
	   var star_count = Math.round(parseFloat(this.field.value));
	   $(this.span_id).innerHTML= "rating saved!";
	   new Effect.Highlight(this.span_id);
	   this._select_stars(star_count);
	},
	
	_select_rating : function(count){
	  var className = "value-"+count;
		for (var i = 0; i < 5; i++){
			this.lis[i].className = (count > i) ? className : "value-0";
		}
	},
	
	setValue: function(rating){
	  rating = Math.round(rating);
		this.value=rating;
		this._select_rating(rating);
		this.options.onRate(rating);
	},
	
	setValue: function(rating){
	  rating = Math.round(rating);
		this.value=rating;
		this._select_rating(rating);
		this.options.onRate(rating);
	},
	
	getValue :function(){
	  return this.value;
	}
}

/////////////////////////
// Page Classes

var StartNewFeedPage = {
  relatedRecordList: null,
  query: null,
  selectedTab: null,
  
  show:function(options){
    var options = options || {};
    if (!options.keepRelatedRecords) {
      StartNewFeedPage.relatedRecordList = options.relatedRecords;
    }
    StartNewFeedPage.query = options.query;
    StartNewFeedPage.selectedTab = options.tab;

    return Page.toggle_content('page_start_new_feed');
  },
  
  toggleSubscriptionType: function(showFeedForm){
		var feed = $('feed_form');
		var smartFeed = $('smart_feed_form');
		if (showFeedForm) {
			feed.show();				
			$('news_feed_query').disable();
			$('news_feed_url').enable();
			smartFeed.hide();
			$('keyword_tips').hide();
			
			$('news_feed_url').focus();
			$('news_feed_url').select();
		} else {
			smartFeed.show();
			$('news_feed_query').enable();
			$('news_feed_url').disable();
			feed.hide();
			$('keyword_tips').show();
			
			$('news_feed_query').focus();
			$('news_feed_query').select();
		}
  }
}


var FeedSearchPage = {
  searchPath: null, // this needs to be initialized in the page
  defaultRelations: null,
  
  showAndSearch: function(query, defaultRelations) {
    Page.subscribe(function(){
      $("feed_query").value = query;
      FeedSearchPage.search(query);
      StartNewFeedPage.relatedRecordList = defaultRelations;
    });
    
    Page.toggle_content('page_find_related_feeds');
  },
  
  search: function(query) {
		$('search_results').hide();
		$('search_activity').show();
		if (!FeedSearchPage.searchPath) {
		  alert('The search function for the feed search page must be set in the page');
		  return;
		}
		
		new Ajax.Request(FeedSearchPage.searchPath, {
        method: 'get',
        parameters: {query: query},
        onSuccess: this._processFeedSearchResult.bind()
      });
	},
	
  _processFeedSearchResult: function(transport){
		if (transport.responseJSON && transport.responseJSON.length > 0) {
		  $('feed_container').innerHTML = '<p>Click the appropriate "Add subscription" link below to subscribe to a feed in this workspace. You can also <a href="#" onclick="return StartNewFeedPage.show({keepRelatedRecords: true, query:$(\'feed_query\').value, tab: \'tracker\'})"><b>Add a keyword tracker</b></a> that will monitor the internet for news that matches these search terms directly.</p>' // reset view
			transport.responseJSON.each(FeedSearchPage._renderSearchResult);
		} else {
			$('feed_container').innerHTML = '<p>No feeds found that match these search terms. <br/><br/><a href="#" onclick="return StartNewFeedPage.show({keepRelatedRecords: true, query:$(\'feed_query\').value, tab: \'tracker\'})"><b>Click here to add a keyword tracker</b></a> that will monitor the internet for news that matches these search terms directly.</b></p>'
		}
		
		$('search_activity').hide();
		$('search_results').show();
		$('search_feed_button').disabled=false;
	},
	
	_renderSearchResult: function(feed) {
	  var group = new Element('div', {'class': 'field-group'});
	  $('feed_container').appendChild(group);
	  
	  var preview = new Element('div', {'class': 'subscription-preview'});
	  group.appendChild(preview);
	  
	  var title = new Element('div', {'class': 'subscription-title'});
	  preview.appendChild(title);
	  title.appendChild(document.createTextNode(feed.title));
	  
	  var description = new Element('div', {'class': 'subscription-description'});
	  preview.appendChild(description);
	  description.appendChild(document.createTextNode(feed.description));
	  
	  var link = new Element('div', {'class': 'subscription-link'});
	  preview.appendChild(link);
	  link.appendChild(document.createTextNode(feed.website.truncate(80)));
	  
	  var url = new Element('div', {'class': 'subscription-url'});
	  preview.appendChild(url);
	  url.appendChild(document.createTextNode(feed.url.truncate(80)));
	  
	  var action = new Element('button');
	  preview.appendChild(action);
	  
	  if (feed.subscribed) {
  	  action.appendChild(document.createTextNode('Already subscribed to this feed'));
  	  action.disabled =true;
	  } else {
  	  action.appendChild(document.createTextNode('Subscribe to this feed'));
	  
  	  Event.observe(action,'click', function(){
        Page.subscribe(function(){
          $('news_feed_url').value = feed.url;
          $('subscription_title').appendChild(document.createTextNode(feed.title));
          $('subscription_url').appendChild(document.createTextNode(feed.url));
          $('news_feed_query').disable();
          $('subscription_description').appendChild(document.createTextNode(feed.description.stripTags()));
        });

        Page.toggle_content("page_new_feed");
      });
    }
  }
}

var ThreatPage = {
  threat_level_name_el: null,
  user_rating_count_el: null,
  
  _threatLevelName: function(value) {
    value = Math.round(value);
    if (value) {
      if (value == 5) return "Very High Threat";
      if (value == 4) return "High Threat";
      if (value == 3) return "Medium Threat";
      if (value == 2) return "Low Threat";
      if (value == 1) return "Very Low Threat";
    } else {
      return "";
    }
  },
  
  showUserRating: function(element, value, register){
    var selector = new RatingSelector(element,'threat',value,{disabled: true});
    if (register)
      selector.register('user_rating');
  },
  
  showThreatLevelName: function(element, value) {
    this.threat_level_name_el = $(element);
    this.threat_level_name_el.innerHTML = this._threatLevelName(value);
  },
  
  showUserRatingCount: function(element, count, url){
    this.user_rating_count_el = $(element);
    this.show_ratings_url = url;
    
    this._updateUserRatingCount(count);
  },
  
  _updateUserRatingCount: function(count) {
    if (count > 0) {
      //var rating_count_text = " ("+count + (count != 1 ?  ' ratings' : ' rating') + ")";
      var rating_count_text = count+" rating" + (count > 1 ? 's' : '');
    
      var script = "new lightbox('"+this.show_ratings_url+"',{width:500,height:300}); return false;";
      var a = Builder.node("a",{href: '#', onclick: script});
      
      this.user_rating_count_el.innerHTML = "";
      this.user_rating_count_el.appendChild(a);
      a.appendChild(document.createTextNode(rating_count_text));
    } else {
      this.user_rating_count_el.innerHTML = "(no ratings)";
    }
  },
  
  showYourRating: function(element, status_element, initial_value, rating_url) {
    this.your_rating_selector = new RatingSelector('your_rating','threat', initial_value,
    {
      onRate: function(value){
          // disable rating until this one is done
          this.your_rating_selector.disableEvents(true);
          new Ajax.Request(rating_url, {
              method: 'post',
              parameters: {value: value},
              onSuccess: function(response) {
                var rObj = response.responseJSON
                // update the rating status
                var rating_status = $(status_element);
                rating_status.innerHTML = "rating saved!";
                new Effect.Highlight(rating_status);
                // update the user ratings
                RatingSelector.getSelectors('user_rating').each(function(selector){
                    selector.setValue(rObj.rating);
                }.bind(this));
                
                // update the user rating count
                this._updateUserRatingCount(rObj.count);
                
                // update the threat level name
                this.threat_level_name_el.innerHTML = this._threatLevelName(rObj.rating);
                
                // re-enable ratings
                this.your_rating_selector.disableEvents(false);
              }.bind(this)
            });
      }.bind(this)
    });
  }
}

var UserEditPage = {
  // shows the confirm password field when editing a user's password
  userChangePasswordConfirm: function (passfield) {
    var new_passfield = Builder.node('input',{type:'password',className:passfield.className,id:passfield.id,name:passfield.name})
    new_passfield.value='';
    passfield.parentNode.insertBefore(new_passfield,passfield);
    passfield.remove();
    new_passfield.focus();
    $('confirm_change_password').show();
    $('confirm_change_password_field').disabled = false;
  }
}

var NewComparisonPage = {
  toggle_comparison_type: function(type) {
    if (type == 'company') {
      $('companies_to_compare').show();
      $('offerings_to_compare').hide();
    } else {
      $('companies_to_compare').hide();
      $('offerings_to_compare').show();
    }
  },
  
  select_unselect_all: function(checkbox_element,type) {
    var select_form = checkbox_element.form;
	  for(var i=0; i<select_form.length;i++){
      if(select_form[i].type == 'checkbox' && select_form[i].name != 'select_all_companies' && select_form[i].name != 'select_all_offerings'){
        if (type == 'company' && select_form[i].name == 'company_ids[]') {
          select_form[i].checked = checkbox_element.checked;
        }
        else if (type == 'offering' && select_form[i].name == 'offering_ids[]') {
          select_form[i].checked = checkbox_element.checked;
        }
	    }
    }
  },
  
  countTrafficCompareables: function(checkbox_element) {
    checkboxes = Form.getInputs(checkbox_element.form, "checkbox");
    var checked_urls = checkboxes.findAll(function(checkbox){ return checkbox.checked; }).pluck("value");    
    if (checked_urls.length == 5) {      
      for (var i=0; i<checkboxes.length; i++) {
        if (checkboxes[i].checked == false) {
          checkboxes[i].disabled = true;
        }
      }
    }
    if (checked_urls.length == 4) {      
      for (var i=0; i<checkboxes.length; i++) {
        if (checkboxes[i].checked == false) {
          checkboxes[i].disabled = false;
        }
      }
    }
  }
  
}

var NewConcernPage = {
  toggle_concern_type: function(type) {
    if (type == 'company') {
      $('companies_selection').show();
      $('products_selection').hide();
    } else {
      $('companies_selection').hide();
      $('products_selection').show();
    }
  }
}

var EditProjectPage = {
  toggle_alias_type: function(auto_alias) {
    var section = $('manual_alias_section');
    
    if (auto_alias) {
      new Effect.BlindUp(section,{duration: Globals.FastEffectDuration, afterFinish: function(){
        $('manual_alias').checked = false;
      }});
      $('auto_alias').checked = true;
      
      this.alias_source_element = $('project_name');
      $('project_alias').value = "";
    } else {
      new Effect.BlindDown(section,{duration: Globals.FastEffectDuration, afterFinish: function(){
        $('project_alias').focus();
        $('manual_alias').checked = true;
      }});      
      
      this.alias_source_element = $('project_alias');
    }
    
    this.update_alias_preview();
  },
  
  setup_alias_preview: function() {
    this.project_name = $('project_name');
    this.project_alias = $('project_alias');
    this.dropbox_alias_preview = $('dropbox_alias_preview');
    
    Event.observe(this.project_name, 'blur', this.update_alias_preview.bind(this));
    Event.observe(this.project_name, 'keyup', this.update_alias_preview.bind(this));
    Event.observe(this.project_alias, 'blur', this.update_alias_preview.bind(this));
    Event.observe(this.project_alias, 'keyup', this.update_alias_preview.bind(this));
  },
  
  update_alias_preview: function() {
    this.dropbox_alias_preview.innerHTML = "";
    var element = this.alias_source_element;
    var alias = this.convert_name_to_alias(element.getValue());
    this.dropbox_alias_preview.appendChild(document.createTextNode(alias));
  },
  
  convert_name_to_alias: function(name) {
    /* keep this in sync with email_in_address version */
    var alias = name.replace(/(?:\..*)/i,'').replace(/(?:[\'\"]|[^\-\w\d])+/g,'-').replace(/(?:^\-+|\-+$)/g,'').replace(/\-{2,}/g,'-').toLowerCase();
    return alias;
  }
}

var NewUserPage = {
  group_select_change_handler: function(select) {
    if (select.value == 'new_group')
      NewUserPage.toggle_add_new_group();
  },
  
  toggle_add_new_group: function() {
    var select_group = $('select_group');
    var new_group = $('new_group');
    
    if (select_group.visible()) {
      select_group.hide();
      new_group.show();
      $('user_group_id').disabled = true;
      $('group_name').focus();
    } else {
      select_group.show();
      new_group.hide();
      $('user_group_id').disabled = false;
      $('user_group_id')[0].selected = true;
    }
  },
  
  toggle_password_type: function(specify_password_type) {
    var specify_password = $('specify_password_section');
    
    if (specify_password_type) {
      new Effect.BlindUp(specify_password,{duration: Globals.FastEffectDuration, afterFinish: function(){
        $('specify_password').checked = false;
      }});
      $('generate_password').checked = true;
    } else {
      new Effect.BlindDown(specify_password,{duration: Globals.FastEffectDuration, afterFinish: function(){
        $('user_password').focus();
        $('specify_password').checked = true;
      }});      
    }
  }
}


var NewCompetitorPage = {

  toggle_add_new_product: function() {      
    $('new_competitor_product_link').hide();
    $('new_competitor_product').show();             
  }
}


var NewOfferingPage = {
    upper_case_company_names: null,
    user_company_name: null,

    setupForm: function(company_names, user_company_name, our_company_nil) {
      NewOfferingPage.our_company_nil = our_company_nil;
      if (user_company_name != null) {
        NewOfferingPage.user_company_name = user_company_name;
      } else {
        NewOfferingPage.user_company_name = ''
      }
      NewOfferingPage.upper_case_company_names = company_names.collect(function(name){
          return name.capitalize();
      });
      
      new Autocompleter.Local(
          'company_name',
          'company_list',
          company_names,
          {choices: 10, ignoreCase: true, partialChars: 1, frequency: 0.1}
      );
      
      Event.observe('offering_name','blur',NewOfferingPage.setupCompanyField.bind());
      Event.observe('company_name','blur',NewOfferingPage.setupCompanyField.bind());
    },
                  
    setupCompanyField: function() {
      var company_url_field = $('company_url_field');
      var company_type_field = $('company_type_field');
      var your_company_msg = $('your_company_msg');
      var company_name = $('company_name').value.capitalize();
      is_existing_company = NewOfferingPage.upper_case_company_names.include(company_name);
      if (!is_existing_company) {
          if (company_name != '') {
            $('company_website_label').innerHTML = $('company_name').value + "'s website <span class=dim>(optional)</span>";
            if (!company_type_field.visible() && NewOfferingPage.our_company_nil) {
              new Effect.BlindDown(company_type_field,{duration: Globals.FastEffectDuration, afterFinish: function() {$('our_company').focus()}});
            }
            if (!company_url_field.visible()) {
              $('company_url').value = '';
              new Effect.BlindDown(company_url_field,{duration: Globals.FastEffectDuration});
            }            
          }
          else
          {
            if (company_type_field.visible()) 
              new Effect.BlindUp(company_type_field,{duration: Globals.FastEffectDuration});
            if (company_url_field.visible()) 
              new Effect.BlindUp(company_url_field,{duration: Globals.FastEffectDuration});
          }          
          if (your_company_msg.visible()) 
            new Effect.BlindUp(your_company_msg,{duration: Globals.FastEffectDuration});
      } else {
          if (company_type_field.visible()) {
            new Effect.BlindUp(company_type_field,{duration: Globals.FastEffectDuration});
          }
          if (company_url_field.visible()) 
            new Effect.BlindUp(company_url_field,{duration: Globals.FastEffectDuration});
          if (!your_company_msg.visible() && company_name == NewOfferingPage.user_company_name.capitalize())
            new Effect.BlindDown(your_company_msg,{duration: Globals.FastEffectDuration});
          if (your_company_msg.visible() && company_name != NewOfferingPage.user_company_name.capitalize())
            new Effect.BlindUp(your_company_msg,{duration: Globals.FastEffectDuration});
      }
    }
}

var NewCompanyPage = {

  upper_case_company_names: null,

  setupForm: function() {     
      Event.observe('company_name','blur',NewCompanyPage.setupCompanyFields.bind());
    },
    
    setupCompanyFields: function() {
      var company_name = $('company_name').value.capitalize();
      if (!company_name == '') {
        $('company_website_label').innerHTML = $('company_name').value + "'s website <span class=dim>(optional)</span>";
      }  
    }

}

var SegmentPage = {
  toggleCategorySelection: function(cell,id) {
    if ($(cell).style.display == 'none') {
      new Effect.BlindDown($(cell),{duration: Globals.FastEffectDuration});
    } else {
      new Effect.BlindUp($(cell),{duration: Globals.FastEffectDuration});
    }
  }
}

var EditComparisonPage = {
  selected_compareables_by_id: [],
  
  setSelectedCompareableIds: function(comparison_type, compareable_ids) {
    this.selected_compareables_by_id = [];
    compareable_ids.each(function(id) {
      $(comparison_type.toLowerCase() + "_id_" + id).checked = true;
      this.selected_compareables_by_id[id] = id;
    }.bind(this));
  },
  
  confirmOfferingChange: function(checkbox)  {
    if (checkbox.checked == false && this.selected_compareables_by_id[checkbox.value]) {
      if (confirm('All data already entered into the comparison for this competitor/product will be lost.\n\nAre you sure you want to remove it from the comparison?')) {
        checkbox.checked = false;
      } else {
        checkbox.checked = true;
      }
    }
    return true;
  },
  
  countTrafficCompareables: function() {
    checkboxes = Form.getInputs('comparison_edit_form', "checkbox");
    var checked_urls = checkboxes.findAll(function(checkbox){ return checkbox.checked; }).pluck("value");    
    if (checked_urls.length == 5) {      
      for (var i=0; i<checkboxes.length; i++) {
        if (checkboxes[i].checked == false) {
          checkboxes[i].disabled = true;
        }
      }
    }
  }
}

var Categorize = {
  setupTags: function() {                
    var data = {
      'Labels': {type:'Label', items:g_categories.collect(function(tag){
             return [tag.name, tag.name];
       })}
    };
    new CategorizeControl($('category'), data, {choices: 10, ignoreCase: true, partialChars: 1, frequency: 0.1});
  }
}

CategorizeControl = Class.create();
CategorizeControl.prototype = {
  initialize: function(element, items, options) {
    options.afterItemSelected = this.submitTag.bind(this);
    options.autoHighlight = true;    
    this.autocomplete = new TypedAutocomplete(element, items, options);
  },
  
  submitTag: function(element, group, type, id, value) {
    $('add_label').onsubmit();
  }
};


// Traffic Class (project traffic)

var CompeteGraph = Class.create();
CompeteGraph.prototype = {
  selected_list: null,
  current_graph_type: null,
  
  initialize: function(initial_list) {
    this.selected_list = initial_list.split('+');
    this.current_graph_type = 'uv';
  },
  
  toggleSelectLink: function() {
    if ($('select_offering_urls').style.visibility == 'hidden') {
      $('select_offering_urls').style.visibility = 'visible';      
    } else {
      $('select_offering_urls').style.visibility = 'hidden';
    }
  },
  
  selectGraphLink: function(link) {
    var all_cells = $$('#traffic_graph_links_table td');
    for (var i=0; i<all_cells.length; i++) {
      all_cells[i].removeClassName('selected');
    }
    link.addClassName('selected');
  },
  
  restoreCheckboxes: function() {
    // go through selected list, check selected urls
    // (called every time the select div is displayed)
    var i = 0;
    while (i < this.selected_list.length) {
      $(this.selected_list[i]).checked = true;
      i++;
    }
  },

  changeGraph: function(image_id,graph_type) {
    this.current_graph_type = graph_type;
    var compete_base_url = 'http://home.compete.com.edgesuite.net/';

    $(image_id).style.visibility = 'hidden';
    image = new Image();
    var image_location = compete_base_url + this.selected_list.join('+'); 
    switch (graph_type) {
      case "uv":
        image_location += '_uv.png';
      break;
      case "rank":
        image_location += '_rank.png';
      break;
      case "sess":
        image_location += '_sess.png';
      break;
      case "avgStay":
        image_location += '_avgStay.png';
      break;
      case "ppv":
        image_location += '_ppv.png';
      break;
      case "att":
        image_location += '_attD_04222007_05222007.png';
      break;
      case "vel":
        image_location += '_vel_02142007.png';
      break;
    }
    
    //Event.observe(image,'error',function(){$(image_id).src='http://home.compete.com.edgesuite.net/site_media/images/invalid_graph.gif';$(image_id).style.visibility = 'visible';});
    //Event.observe(image,'load',function(){$(image_id).src=image.src;setTimeout(function(){$(image_id).style.visibility = 'visible';}, 150);});
    // not observing these events since it doesnt work in safari - just using event handlers directly
    image.onerror = function(){$(image_id).src='http://home.compete.com.edgesuite.net/site_media/images/invalid_graph.gif';$(image_id).style.visibility = 'visible';}
    image.onload = function(){$(image_id).src=image.src;setTimeout(function(){$(image_id).style.visibility = 'visible';}, 150);}
    image.src = image_location; // sets the location, and the Image starts fetching
  },
  
  compareURL: function(image_id,url) {
  
    // drop url from list if just deselected
    var i = 0;
    var deselected = false;
    while (i < this.selected_list.length) {
      if (this.selected_list[i] == url) {
        deselected = true;
        this.selected_list.splice(i, 1);
      } else {
        i++;
      }
    }
    
    // if the url was deselected
    if (deselected) {
      this.changeGraph(image_id,this.current_graph_type);
      
      // if 5 offerings were selected (and rest of links were inactive), set other links back to active
      if (this.selected_list.length == 4) {
        var all_checkboxes = $$('#offering_urls input');
        for (var i=0; i<all_checkboxes.length; i++) {
          if (all_checkboxes[i].checked == false) {
            all_checkboxes[i].disabled = false;
          }
        }
      }
      
      
    } else { // otherwise select the url
      if (this.selected_list.length < 5) { // can only compare up to 5 urls
        this.selected_list.push(url);
        this.changeGraph(image_id,this.current_graph_type);
      }
      
      // if 5 offerings now selected, don't allow any others to be selected
      if (this.selected_list.length == 5) {
        var all_checkboxes = $$('#offering_urls input');
        for (var i=0; i<all_checkboxes.length; i++) {
          if (all_checkboxes[i].checked == false) {
            all_checkboxes[i].disabled = true;
          }
        }
      }
      
    }
    
    // save current selection in a cookie
    var value = this.selected_list.join(',');
		var date = new Date();
		date.setTime(date.getTime()+(365*24*60*60*1000));
		var expires = date.toGMTString();
	  document.cookie = "traffic_comparison="+value+"; expires="+expires+"; path=/";
    
  }
}

var SegmentOfferingPage = {

  editType: function() {
    $('show_segment_offering_type').style.display = 'none';
    $('edit_segment_offering_type').style.display = 'block';
  },
  
  showType: function() {
    $('edit_segment_offering_type').style.display = 'none';
    $('show_segment_offering_type').style.display = 'block';
  },

  hideShowOfferingNeeds: function() {
    $('needs_min_height').style.height = $('show_offering_needs').offsetHeight + 'px';
    new Effect.BlindUp('show_offering_needs', {duration: Globals.FastEffectDuration, queue: 'front'});  
  },
  
  showShowOfferingNeeds: function() {
    new Effect.BlindDown('show_offering_needs', {duration: Globals.FastEffectDuration, queue: 'end'});  
  },
  
  hideShowSegmentOfferingChannels: function() {
    $('channels_min_height').style.height = $('show_segment_offering_channels').offsetHeight + 'px';
    new Effect.BlindUp('show_segment_offering_channels', {duration: Globals.FastEffectDuration, queue: 'front'});    
  },
  
  showShowSegmentOfferingChannels: function() {
    new Effect.BlindDown('show_segment_offering_channels', {duration: Globals.FastEffectDuration, queue: 'end'});
  }
    
}

var NewPostPage = {
  defaultLink: null,
  defaultFile: null,
  tagField: null,
  defaultRelation: null,
  
  postLink: function(link) {
    if (!link || (Object.isString(link) && link.strip() == "")) {
      alert("You must enter a link address");
      new ActivityIndicator().hide();
      Page.toggle_content('page_new_link'); // hide it
      Page.toggle_content('page_new_link'); // reshow it
      return false;
    }
    
    NewPostPage.defaultLink = link;
    
    Page.toggle_content('page_new_post');
    
    return false;
  },
  
  // must use entire field because of browser security issues with setting value
  postFile: function(fileField) {
    NewPostPage.defaultFile = fileField;
  
    Page.toggle_content('page_new_post');
    
    return false;
  },
  
  newProfilePost: function(relation_sid, trigger) {
    var record = current_project.find_record_by_sid(relation_sid);
    NewPostPage.defaultRelation = record.type + "_" + record.id;
    Page.toggleInlineContent("new_inline_post", "new_item", trigger);
  },
  
  setupAttachments: function(temp_attachments, attachments) {
    this.attachment_control = new AttachmentControl('attachments',temp_attachments,attachments);
  },
  
  setupTagging: function(fieldId, buttonId, relatedRecordListId, tagListId, selectedTagsId) {
    var data = {
      'Companies': {type:'Company', items:current_project.companies.collect(function(company){
          return [company.id, company.name];
      })},
      'Products': {type:'Offering', items:current_project.offerings.collect(function(offering){
          return [offering.id, offering.name];
      })},
      'Customer Types': {type:'Segment', items:current_project.segments.collect(function(segment){
          return [segment.id, segment.name];
      })},
      'Labels': {type:'Label', items:g_tags.collect(function(tag){
           return [tag.name, tag.name];
      })}
    };
    
    if (fieldId) {
      // this needs to be global so that the project_tag_data can update it for the bookmarklet
      this.tagField = new TagField(
        fieldId,
        buttonId,
        relatedRecordListId,
        tagListId,
        selectedTagsId,
        data,
        {choices: 10, ignoreCase: true, partialChars: 1, frequency: 0.1}
      );
    } else {
      this.tagField.reset(data);
    }
  }         
}

var NewFeedPage = {
  setupTagging: function() {
    var data = {
            'Companies': {type:'Company', items:current_project.companies.collect(function(company){
                return [company.id, company.name];
            })},
            'Products': {type:'Offering', items:current_project.offerings.collect(function(offering){
                return [offering.id, offering.name];
            })},
            'Customer Types': {type:'Segment', items:current_project.segments.collect(function(segment){
                return [segment.id, segment.name];
            })},
            'Labels': {type:'Label', items:g_tags.collect(function(tag){
                 return [tag.name, tag.name];
            })}
        };

    new TagField(
      'project_news_feed_tag_field',
      'project_news_feed_add_tag_button',
      'project_news_feed_related_record_list',
      'project_news_feed_feed_tag_list',
      'selected_tags',
      data,
      {choices: 10, ignoreCase: true, partialChars: 1, frequency: 0.1}
    );
  }
}

var AccountContentLegalNotice = {  
  confirm: function() {
    if (current_account.confirmation_notice) {
      if ($('post_confirmation').checked) {
        return true;
      } else {
        alert('You must check the required agreement before the post can be submitted.')
        window.setTimeout(function(){$('submit_post').disabled = false;}, 150);// for safari
        setTimeout("$('activity_indicator').hide()",200);
        return false;
      }
    }
  },
  
  showConfirmationCheckbox:function() {
    if (current_account.confirmation_notice) {
      $('confirmation_checkbox').innerHTML = '<div class="field-group"><div class="field"><table><tr><td valign="top"><input type="checkbox" id="post_confirmation"></td><td><label for="post_confirmation">' + current_account.confirmation_notice + '</label></td></tr></table></div></div>'  
    }
  }
}


var NewClippingPage = {
  
  setupNewClipping: function(clipping) {
    if (clipping['title'])
      $('clipping_title').value = clipping['title'];
    
    if (clipping['url'])
      $('clipping_url').value = clipping['url'];
      
    if (clipping['description'])
      $('clipping_description').value = clipping['description'].strip();
    
    if (clipping['related_record_list'])
      $('clipping_related_record_list').value = clipping['related_record_list'];
      
    if (clipping['tag_list'])
      $('clipping_clipping_tag_list').value = clipping['tag_list'];
  },
  
  // rjs calls this because it may not be shown anyways so we need to check if it exists
  hideAddClippingLink: function() {
    if ($("page_add_clipping_link")) 
      Page.toggleInlineContent('page_add_clipping_link')
  }  
}

var PermissionsPage = {

  showUserPermissions: function(user_id) {
    var showrow = 'show_user_permissions_' + user_id;
    $(showrow).style.display = '';
    var editrow = 'edit_user_permissions_' + user_id;
    $(editrow).style.display = 'none';
  },
  
  editUserPermissions: function(user_id) {
    var showrow = 'show_user_permissions_' + user_id;
    $(showrow).style.display = 'none';
    var editrow = 'edit_user_permissions_' + user_id;
    $(editrow).style.display = '';
  },
  
  showGroupPermissions: function(group_id) {
    var showrow = 'show_group_permissions_' + group_id;
    $(showrow).style.display = '';
    var editrow = 'edit_group_permissions_' + group_id;
    $(editrow).style.display = 'none';
  },
  
  editGroupPermissions: function(group_id) {
    var showrow = 'show_group_permissions_' + group_id;
    $(showrow).style.display = 'none';
    var editrow = 'edit_group_permissions_' + group_id;
    $(editrow).style.display = '';
  }
  
}


var SwotPage = {
  toggleDescription: function(swot_id) {
    description = 'swot_desc_' + swot_id;
    toggle_link = 'swot_toggle_' + swot_id;
    if ($(description).style.display == 'none') {
      new Effect.BlindDown($(description),{duration: Globals.FastEffectDuration});
      $(toggle_link).innerHTML = '(-)';
    } else {
      new Effect.BlindUp($(description),{duration: Globals.FastEffectDuration});      
      $(toggle_link).innerHTML = '(+)';
    }
  }
}

var ConcernPage = {
  toggleConcernSolutionEdit: function() {
    if ($('edit_reason_closed_link')) {
      if ($('edit_reason_closed_link').style.visibility == 'hidden') {
        $('edit_reason_closed_link').style.visibility = 'visible';    
        $('edit_reason_closed').hide();  
        $('concern_reason_closed').show();  
      } else {
        $('edit_reason_closed_link').style.visibility = 'hidden';
        $('concern_reason_closed').hide();
        $('edit_reason_closed').show();  
      }  
    }
  }
}

var NewCustomerPage = {
  type_select_change_handler: function(select) {
    if (select.value == 'new_type')
      NewCustomerPage.toggle_add_new_type();
  },
  
  toggle_add_new_type: function() {
    var select_type = $('select_type');
    var new_type = $('new_type');
    
    if (select_type.visible()) {
      select_type.hide();
      new_type.show();
      $('customer_segment_id').disabled = true;
      $('segment_name').focus();
    } else {
      select_type.show();
      new_type.hide();
      $('customer_segment_id').disabled = false;
      $('customer_segment_id')[0].selected = true;
    }
  }
}

var WikiPage = {
  toggleDiff: function(value) {
    if (!value) {
      var hide = $('wiki_diff');
      var show =$('wiki_content');
    } else {
      var hide = $('wiki_content');
      var show =$('wiki_diff');
    }
    
    hide.hide();
    show.show();
  }
}

var Help = {

  toggleInlineHelp: function() {                    
    var inline_help_wrapper = $('inline_help_wrapper');    
    if (inline_help_wrapper.visible()) {
      new Effect.BlindUp(inline_help_wrapper,{duration: Globals.FastEffectDuration});
    }    
  }
}

/////////////////
// Effects

Effect.BlindUpAndFade = function(element) {
  return new Effect.Parallel(
    [
      new Effect.BlindUp(element),
      new Effect.Fade(element)
    ],
    arguments[1] || {}
  );
}

Effect.BlindDownAndAppear = function(element) {
  return new Effect.Parallel(
    [
      new Effect.BlindDown(element),
      new Effect.Appear(element)
    ],
    arguments[1] || {}
  );
}

Effect.AppearFromInvisible = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('visibility') == 'hidden' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).setStyle({visibility:''});
  }}, arguments[1] || { });
  return new Effect.Opacity(element,options);
}

Effect.FadeToInvisible = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
    from: element.getOpacity() || 1.0,
    to:   0.0,
    afterFinishInternal: function(effect) { 
      if (effect.options.to!=0) return;
      effect.element.setStyle({opacity: oldOpacity, visibility: 'hidden'}); 
    }
  }, arguments[1] || { });
  return new Effect.Opacity(element,options);
}

Effect.Scroll = Class.create();
Object.extend(Object.extend(Effect.Scroll.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    this.start(Object.extend({x: 0, y: 0}, arguments[1] || {}));
  },
  setup: function() {
    var scrollOffsets = (this.element == window) 
                ? document.viewport.getScrollOffsets() 
                : Element._returnOffset(this.element.scrollLeft, this.element.scrollTop) ;
    this.originalScrollLeft = scrollOffsets.left;
    this.originalScrollTop  = scrollOffsets.top;
  },
  update: function(pos) {
    this.element.scrollTo(Math.round(this.options.x * pos + this.originalScrollLeft), Math.round(this.options.y * pos + this.originalScrollTop));
  }
});

//////////////////////
// Activity Indicator

Ajax.Responders.register({
  onCreate: function(request, transport, json){
    var indicator = $('activity_indicator');
    
    if (indicator.style.display == 'none') {
      if (request.method == "post" || request.method == "put") {
        new ActivityIndicator("Saving...").show();
      } else {
        new ActivityIndicator("Loading...").show();
      }
    
      indicator.show();
    }
    // TODO: if the browser does not support position fixed then use a timeout to update the position of the element
  },
  
  onComplete: function(request, transport, json){
    if (Ajax.activeRequestCount == 0) {
      $('activity_indicator').hide();  
    }

    try {
      if (transport.status == 404 || transport.status == 403) {
        alert(transport.responseText);
      } else if (transport.status == 500) {
        alert('The service is experiencing temporary issues. Please try your request again in a few moments.');
      }
    } catch (e) {
      alert('The service is experiencing temporary issues. Please try your request again in a few moments.');
    }
  }
});


////////////////
// UTILITY

Element.addMethods(["INPUT","TEXTAREA"],{
  setEmptyText: function(element,text) {
    if (element.getValue() == null || element.getValue() == "") {
      element.addClassName('blank');
      element.value = text;
    }
    
    Event.observe(element,'focus',function() {
      if (element.value == text) {
        element.value = "";
        element.removeClassName('blank');
      }
    });
    
    Event.observe(element,'blur',function() {
      if (element.value == "") {
        element.value = text;
        element.addClassName('blank');
      }
    });
    
    element.getValue = function() {
      return this.value == text ? '' : this.value;
    }.bind(element);
  }
});

Element.addMethods(["TEXTAREA"],{
  autoExpand: function(textarea){
    var textareaMargin = 20;
    var doubleTextareaMargin = textareaMargin * 2;
    
    // get the initial height
    var minHeight = parseInt(textarea.getStyle('height'));

    var mirror = new Element('div');
    styles = {};
    ['lineHeight', 
     'paddingTop', 'paddingLeft', 'paddingRight', 'paddingBottom', 
     'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth', 
     'fontSize', 
     'fontFamily', 
     'width'].each(function(style){
      styles[style] = textarea.getStyle(style);
    });
    styles['white-space'] = 'nowrap';
    styles['position'] = 'absolute';
    styles['visibility'] = 'hidden';
    mirror.setStyle(styles);
    
    // disable resizing in safari
    textarea.setStyle({resize: 'none'});
    
    // debugging
    // alert($H(styles).inspect()); 
    //textarea.parentNode.insertBefore(mirror,textarea);
    //mirror.setStyle({visibility: '', position: 'absolute'});
    document.body.appendChild(mirror);
    
    // compute the height of a normal line
    mirror.innerHTML = "a";
    var lineHeight = parseInt(mirror.getStyle('height'));
    
    var expandAsNeeded = function (textarea, value, doNotScroll) {
      var viewportHeight = document.viewport.getHeight();
      var maxHeight = viewportHeight - doubleTextareaMargin;
      
      if (value.length > 3000) {
        // too long, just set it to max height
        // TODO: integrate this with the code below so it
        // scrolls as appropriate etc.
        textarea.setStyle({height:maxHeight +"px"});
        return;
      }
      
      
      var html = value.escapeHTML().gsub(/[ \t][ \t]/,'&nbsp;&nbsp;').gsub(/\n/,'<br />') + "&nbsp";
      mirror.innerHTML = html;
      mirror.setStyle({height:""}); // reset height incase we set it below
      
      // need to reset the mirror width in case the textarea had a percentage width
      var textareaWidth = textarea.getWidth();
      mirror.setStyle({width: textareaWidth+"px"});
      var mirrorWidth = mirror.getWidth();
      if (mirrorWidth > textareaWidth)
        mirror.setStyle({width: textareaWidth - (mirrorWidth - textareaWidth) + "px"});
      
      var viewportHeight = document.viewport.getHeight();
      var maxHeight = viewportHeight - doubleTextareaMargin;
      var mirrorHeight = mirror.getHeight();
      var mirrorStyleHeight = parseInt(mirror.getStyle('height'));
      var extraSpace = lineHeight * 1.4;
      var targetHeight = mirrorHeight + extraSpace;
      var targetStyleHeight = mirrorStyleHeight + extraSpace;
      
      if (targetHeight > maxHeight) {
        targetStyleHeight -= (targetHeight - maxHeight);
      }
      
      var textareaStyleHeight = parseInt(textarea.getStyle("height"));
      targetStyleHeight = Math.max(minHeight,targetStyleHeight)
      
      if (targetStyleHeight > textareaStyleHeight) {
        textarea.setStyle({height: targetStyleHeight + "px"});
        var bottom = textarea.viewportOffset()[1] + textarea.getHeight();
        var overflow = bottom+textareaMargin-viewportHeight;
        if (overflow > 0 && !doNotScroll) {
          var scrollOffset = document.viewport.getScrollOffsets();
          window.scrollTo(scrollOffset[0],scrollOffset[1]+overflow);
        }
      } else if (targetStyleHeight < textareaStyleHeight) {
        textarea.setStyle({height: targetStyleHeight + "px"});
      }
    }

    new Form.Element.Observer(
      textarea,
      0.2,  // 200 milliseconds
      expandAsNeeded);
      
    expandAsNeeded(textarea, textarea.value, true/* don't scroll */);
  }
});

Event.keyCode = function(event) {
    return event.which || event.keyCode;
};

Event.ascii = function(event) {
    var decimal = Event.keyCode(event);
    return decimal>=32 ? String.fromCharCode(decimal) : '';
};

var _unique_id = 1;
function unique_id(){
	return _unique_id++;
}

function hover_go(e,url) {
    var el = Event.element(e);
    if (typeof(url) == "string") { ReturnTo.go(url); }
    if (el && el.tagName && el.tagName != "A") {
      if (typeof(url) == "string") {
        window.location=url;
      } else {
        url();
      }
    }
}

function waitForThumbnail(id,path) {
  window.setTimeout(function(){
    $(id).innerHTML = "<img src='"+path+"'/>";
    $(id).removeClassName('thumbnail-loading')
  },2000) // try loading the image in 2 seconds
}

TextAreaExpander = Class.create();
TextAreaExpander.prototype = {
  initialize: function(element, textarea) {
    this.textarea = $(textarea);
    
    this.anchor = new Element('a',{'href':'#'}).update("(expand text box)");
    $(element).appendChild(this.anchor);
    
    this.anchor.onclick = function() { return false; }
    Event.observe(this.anchor,'click',this.expand.bind(this));
  },
  
  expand: function(textarea) {
    textarea = this.textarea;
    current_height = textarea.getStyle('height');
    textarea.style.height = (parseFloat(current_height) * 2).round() + "px";
    this.anchor.hide();
  }
};

window.location.querystring = (function() {
  var result = $H();  
  var querystring = location.search.gsub(/\+/,'%20');  
  if (!querystring)
      return result;
  
  var queryParams = $H(querystring.toQueryParams());
      
  return queryParams;
})();

window.location.overwriteParams = function(params) {
  var querystring = window.location.querystring.merge(params || {});
  querystring.keys().each(function(key){
    if (querystring.get(key) == null)
      querystring.unset(key);
  })
  
  if (querystring.keys().length > 0)
    return window.location.href.split(/[\?\#]/)[0] + "?" + querystring.toQueryString();
  else
    return window.location.href.split(/[\?\#]/)[0];
};

var Cookie = {
  set: function(name, value, extra_options) {
    var options = {
      path: '/',
      daysToExpire: null
    };
    Object.extend(options,extra_options || {});
    
    var daysToExpire = options['daysToExpire'];
    var path = options['path'];
    
    var expire = '';
    if (daysToExpire != undefined) {
      var d = new Date();
      d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
      expire = '; expires=' + d.toGMTString();
    }
    
    var path = options['path'] ? "; path="+escape(path) : '';
    return (document.cookie = escape(name) + '=' + escape(value || '') + expire + path);
  },
  get: function(name) {
    var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
    return (cookie ? unescape(cookie[2]) : null);
  },
  erase: function(name) {
    var cookie = Cookie.get(name) || true;
    Cookie.set(name, '', -1);
    return cookie;
  },
  accept: function() {
    if (typeof navigator.cookieEnabled == 'boolean') {
      return navigator.cookieEnabled;
    }
    
    Cookie.set('_test', '1');
    return (Cookie.erase('_test') === '1');
  }
};

var JSONCookie = {
  set: function(name, value, daysToExpire) {
    Cookie.set(name, value.toJSON(), daysToExpire);
  },
  
  get: function(name) {
    var value = Cookie.get(name);
    if (value)
      return value.evalJSON(true);
    else
      return null;
  }
};

var JSONData = {
  loading: new Hash(),
  
  load: function(path){
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = path;
    this.loading.set(path,true);
    document.getElementsByTagName('head')[0].appendChild(script);
  },
  
  complete: function(path){
    if (this.loading.get(path))
      this.loading.unset(path);
  },
  
  onLoad: function(callback){
    if (this.loading.keys().length > 0) {
      this.onLoad.bind(this,callback).delay(.1);
      return;
    }
    
    callback();
  }
};


/// resizing functions for wide layouts

document.doOnScroll = function (e) {
  if (document.viewport.getScrollOffsets()[0] > 0) {
      if (initial_width == 0) {
          initial_width = document.viewport.getWidth();
      }

      var new_width = initial_width + document.viewport.getScrollOffsets()[0];
      var text_width =  "" + new_width + "px";
      document.body.style.width = text_width;
  	  $('tabs').style.width = text_width;
      scrolled = 1;
  }

}

document.doOnResize = function(e){
	if (scrolled == 1) {
	
		if (self.innerHeight) // all except Explorer
		{
			inner_width = document.documentElement.clientWidth;
		}
		else 
			if (document.documentElement && document.documentElement.clientHeight) // Explorer 6 Strict Mode
			{
				inner_width = document.documentElement.clientWidth;
			}
			else 
				if (document.body) // other Explorers
				{
					inner_width = document.body.clientWidth;
				}
		
		var content_width = Element.getDimensions('comparison_matrix_table').width;
		if (inner_width > content_width) {
			document.body.style.width = "100%";
			$('tabs').style.width = "100%";
			scrolled = 0;
		}
		else {
			var header_width = inner_width + document.viewport.getScrollOffsets()[0];
			document.body.style.width = header_width + "px";
			$('tabs').style.width = header_width + "px";
		}
		initial_width = 0;
	}
}


var Flash = {
  notice: function(html) {
    this.show(html,'notice');
  },
  
  error: function(html) {
    this.show(html,'error');
  },
  
  show: function(html,type) {
    var container = $('flash_container');
    container.update("<div id='"+type+"'><div class='notice-inner'>"+html+"</div></div>");
    
    var moveFlashIntoView = function(){
      var topOffset = container.viewportOffset().top
      if (topOffset < 0) {
        this.originalTop  = parseFloat(container.getStyle('top')  || '0');
      
        var height = container.getDimensions().height;
        var moveY = 0;
        var moveY2 = 0;
        // move it to just above the edge then slide it in
        if (topOffset + height < 0) {
          moveY2 = (-topOffset - height);
          container.setStyle({
            top:  moveY2 + 'px'
          });
          moveY = height;
        } else {
          moveY = -topOffset;
        }
      
        // now slide it into view
        new Effect.Move (container,{ y: moveY, duration: 0.3});
        // side it back out of view
        new Effect.Move (container,{ y: (-moveY), duration: 0.3,  queue: 'end', delay: 3});
        // and slide it to the original position
        if (moveY2 != 0) {
          new Effect.Move (container,{ y: (-moveY2), duration: 0.3,  queue: 'end'});
        }
      }
    }
    // we delay this to give any inline page content etc time to animate first before moving
    moveFlashIntoView.delay(Globals.FlashDelay); 
  }
}

var PageEvent = {
  listenersByEvent: new Hash(),

  observe: function(name,callback){
    var listeners = this.listenersByEvent.get(name);
    if (listeners) {
      listeners.push(callback);
    } else {
      listeners = [callback];
      this.listenersByEvent.set(name,listeners);
    }
  },
  
  fire: function(name) {
    var listeners = this.listenersByEvent.get(name);
    
    var args = $A(arguments);
    var obj = args.shift();
    
    if (listeners) {
      listeners.each(function(listener){
        listener.apply(obj, args);
      });
    }
  }
};

var ReturnTo = {
  fromName: null,
  
  set: function(fromName) {
    this.fromName = fromName;
  },
  
  go: function(url) {
    if (!this.fromName) return true;
    
    var fromPath = window.location.pathname + window.location.search;
    var fromName = this.fromName;
    
    var toPath = this.normalizePath(url);
    
    var h = $H();
    h.set(toPath,[fromPath,fromName]);
    
    JSONCookie.set('return_to',h);
  },
  
  normalizePath: function(url) {
    return "/"+url.gsub(/#.*/,"").gsub(/.+?\:\/\/.+?\//,"").gsub(/^\//,'');
  }
}

Page.subscribe(function(){
  var hash = JSONCookie.get('return_to');

  var path = window.location.pathname + window.location.search;

  if (hash && hash[path]) {
    var el = $('breadcrumb');
    if (el) {
      el.update("");
      el.appendChild(new Element('a',{'class':'return-link','href':hash[path][0]}).update("<span>&#171;</span> " + hash[path][1]));
    }
  }
});

var GoogleCharts = {
  simpleEncoding: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
  
  simpleEncode: function(valueArray, maxValue) {
    var simpleEncoding = GoogleCharts.simpleEncoding;
    var chartData = ['s:'];
    for (var i = 0; i < valueArray.length; i++) {
      var currentValue = valueArray[i];
      if (!isNaN(currentValue) && currentValue >= 0) {
        chartData.push(simpleEncoding.charAt(Math.round((simpleEncoding.length-1) * currentValue / maxValue)));
      } else {
        chartData.push('_');
      }
    }
    return chartData.join('');
  },
  
  chart: function(el, values) {
    var max = values.max();
    
    var img = new Element('img');
    img.src = "http://chart.apis.google.com/chart?";
    img.src += "chs=250x100";
    img.src += "&chd="+GoogleCharts.simpleEncode(values,max);
    img.src += "&cht=lc";
    //img.src += "&chg=" + (10) + "," + (30) + ",1,0";
    img.src += "&chxt=y";
    img.src += "&chxr=0,0,"+max;
    //img.src += "&chxl=0:|Jan|Feb|June";
    img.src += "&chf=bg,s,F2F2F2";
    
    $(el).appendChild(img);
  },
  
  sparkline: function(el, values) {
    var max = values.max();
    
    var img = new Element('img');
    img.src = "http://chart.apis.google.com/chart?";
    img.src += "chs=100x30";
    img.src += "&chd="+GoogleCharts.simpleEncode(values,max);
    img.src += "&cht=ls";
    //img.src += "&chg=" + (10) + "," + (30) + ",1,0";
    img.src += "&chxt=r";
    img.src += "&chxr=0,0,"+max;
    
    $(el).appendChild(img);
  }
};

var RelationList = Class.create();
RelationList.prototype = {
  initialize: function(element, sids) {
    var el = $(element);
    var names = sids.collect(function(sid){
      var record = current_project.find_record_by_sid(sid);
      return record.name;
    });
    el.appendChild(document.createTextNode(names.join(", ")));
  }
}

function insert_fields(link, method, content, container_id) {
  var new_id = new Date().getTime();
  var regexp = new RegExp("new_" + method, "g")
	$(container_id).insert({
    after: content.replace(regexp, new_id)
  });
}

function remove_fields(link) {
  var hidden_field = $(link).previous("input[type=hidden]");
  if (hidden_field) {
    hidden_field.value = '1';
  }
  $(link).up(".fields").hide();
}

function updatePositionHiddenField(tbody) {
	var position = 1;
	var rows = $A(tbody.children);
	rows.each(function(row) {
		row.down('.position').value = position;
		position++;
	});
}

Array.prototype.clean = function(deleteValue) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] == deleteValue) {         
      this.splice(i, 1);
      i--;
    }
  }
  return this;
};
