
//
// Global variables
//

var
  
  WINDOWS                                = {} ,
  USER_LOGGED_IN                         = false ,
  SHOULD_UPDATE_WINDOW_SIZE              = true ,
  SHOULD_TRY_TO_FOCUS_FIRST_FORM_ELEMENT = true ,
  MAIN_TOOLTIP ,
  PANEL_INSTANCES                        = $H({}) ,
  IPE_INSTANCES                          = $H({}) ,
  DEFAULT                                = undefined ,
  
  selection = new tinymce.dom.Selection( new tinymce.DOM.DOMUtils(document) ,
    window ,
    new tinymce.dom.Serializer()
  )
  
;

//
// Prototype extensions
//

CustomEnumerableExtensions = {
  count: function() { return this.size() }
}
Object.extend( Array.prototype, CustomEnumerableExtensions ) ;
Hash.addMethods(CustomEnumerableExtensions) ;

Object.extend( Array.prototype, {
  second: function() { return this[1] } ,
  third: function() { return this[2] }
} ) ;

Object.extend( Event.Methods, (function(){
  return {
    stopThisObserver: function( event, element ) {
      var
        event   = Event.extend(event) ,
        element = $(element) ,
        caller  = arguments.callee.caller
      ;
      element.stopObserving( event.type, caller ) ;
      return event ;
    }
  }
})() ) ;

Object.extend( Event, Event.Methods ) ;

Element.addMethods({
  
  makeHidden: function( element, but_displayable ) {
    element = $(element) ;
    if ( Object.isUndefined(but_displayable) ) but_displayable = true ;
    element.style.visibility = 'hidden' ;
    if (but_displayable) element.style.display = '' ;
    return element ;
  } ,
  
  makeVisible: function( element, but_not_displayable ) {
    element = $(element) ;
    if ( Object.isUndefined(but_not_displayable) ) but_not_displayable = true ;
    var style = { visibility: 'visible' } ;
    if (but_not_displayable) style.display = 'none' ;
    element.setStyle(style) ;
    return element ;
  } ,
  
  _insert: Element.insert ,
  _update: Element.update ,
  
  insert: function( element, insertions ) {
    element = $(element) ;
    element._insert(insertions) ;
    Element.afterInsert.bind( element, insertions ).defer() ;
    return element ;
  } ,
  
  update: function( element, content ) {
    element = $(element) ;
    Element.beforeUpdate.bind( element, content )() ;
    element._update(content) ;
    return element ;
  }
  
}) ;

var klass = Ajax.InPlaceEditor ;
klass.addMethods({
  
  createErrorPanel: createErrorPanelForIPE.methodize() ,
  displayError: function(text) {
    this.createErrorPanel( this.title + ' ' + text ) ;
  } ,
  
  _handleFormSubmission: klass.prototype.handleFormSubmission ,
  handleFormSubmission: function(event) {
    
    var
      type  = this.type ,
      value = $F( this._controls.editor )
    ;
    
    if ( type.include('number') ) {
      if ( isNaN(value) ) {
        this.displayError('must be a number') ;
        return false ;
      }
      if ( type.include('positive') ) {
        if ( value < 1 ) {
          this.displayError('must be a <b>positive</b> number') ;
          return false ;
        }
      }
    }
    
    this._handleFormSubmission(event) ;
    
  }
  
}) ;

Object.extend( Droppables, {
  removeById: function(id) {
    this.drops = this.drops.reject( function(d) {
      return d.element.id == id
    } ) ;
  }
} ) ;

//
// Window specific functions
//

function is_user_logged_in () {
  if ( USER_LOGGED_IN === false ) {
    createInfoDialog( 'You must be logged in to perform this operation!' ) ;
  }
  return USER_LOGGED_IN ;
}

function createInfoDialog( text, options ) {
  
  var
    options               = $H(options) ,
    id                    = options.unset(id) || 'InfoDialogWindow' ,
    words                 = text.split(/\s+/).reject( function(x) {
        return x.match( /^[-!"#$%&'()*+,./:;<=>?@[\\\]_`{|}~]+$/ ) || x.blank() ;
      } ) ,
    countOfWordsPerSecond = 4 ,
    secondsToClose        = Math.round( words.count() / countOfWordsPerSecond ) + 1
  ;
  
  Dialog.info(
    '<div class="inner"> ' + text +
    '<div class="info_notice"> The window will close itself in <strong>' +
    secondsToClose + '</strong> seconds ... </div></div>' , {
      id: id ,
      className: 'alphacube' ,
      width: 350 ,
      closable: true ,
      onShow: function() {
          if ( !WINDOWS[id] ) {
            SHOULD_UPDATE_WINDOW_SIZE = true ;
            WINDOWS[id] = this ;
            update_window_size() ;
          }
        } ,
      onClose: function() {
          delete WINDOWS[id] ;
        }
    }
  ) ;
  
  Dialog.closeInfo.bind(Dialog).delay(secondsToClose) ;
  
}

function create_window ( id, url ) {
  
  var win = WINDOWS[id] ;
  
  if ( ! win ) {
    
    win = new Window({
      id: id ,
      className: 'alphacube' ,
      // width: 'auto' ,
      // height: 'auto' ,
      // minWidth: 'auto' ,
      // minHeight: 'auto' ,
      onShow: window_on_show ,
      // showEffect: Effect.BlindDown ,
      hideEffect: Effect.SwitchOff ,
      minimizable: false ,
      maximizable: false
    } ) ;
    
    new Ajax.Request( url, {
      method: 'get',
      onCreate: function(transport) { ajax_before() } ,
      onSuccess: function(transport) {
        win.content.update( transport.responseText ) ;
        Windows.focus(id) ;
        // win.show(true) ;
        window_ajax_complete(transport) ;        
      } ,
      onFailure: function(transport) { ajax_failure(transport) }
    }) ;
    
    WINDOWS[id] = win ;
    
  } else {
    SHOULD_UPDATE_WINDOW_SIZE = true ;
    SHOULD_TRY_TO_FOCUS_FIRST_FORM_ELEMENT = true ;
    win.showCenter(true) ;
  }
  
}

function window_on_show (win) {
  try_to_focus_first_form_element_within( win.element ) ;
}

function window_ajax_complete(xhr) {
  ajax_status_check( xhr, function(xhr) {
    var container_id = Windows.getFocusedWindow().getId() ;
    update_window_size() ;
    _add_notice_error_observers(container_id) ;
  } ) ;
}

function update_window_size() {
  
  var win = Windows.getFocusedWindow() ;
  if ( !win || !SHOULD_UPDATE_WINDOW_SIZE ) return ;
  
  win.element.setStyle({ display: 'block', visibility: 'hidden' }) ;
  win.content.setStyle({ overflow: 'visible' }) ;
  
  // we are trying to execute this block of code
  // because of inner could be null
  try {
    var inner = win.content.select('.inner').first() ;
    win.setSize( inner.getWidth(), inner.getHeight() ) ;
    inner.setStyle({ position: 'static' }) ;
  } catch(e) {}
  
  win.content.setStyle({ overflow: 'hidden' }) ;
  win.element.setStyle({ display: 'none', visibility: 'visible' }) ;
  
  win.showCenter( win.visible ? false : true ) ;
  
}

//
// Menu specific functions
//

function open_sub_menu (id) {
  
  var menu_id     = 'BrandsMenu_' + id ;
  var sub_menu_id = 'BrandsSubmenu_' + id ;
  
  var plus_button_value = ( $(sub_menu_id).getStyle('display') == 'block' ) ? '+' : '-' ;
  $(menu_id).select('.plus_button')[0].update( plus_button_value ) ;
  
  Effect.toggle( sub_menu_id, 'blind', {duration:0.5} ) ;
  
}

//
// Cart specific functions
//

var Cart = Class.create({}) ;
Object.extend( Cart, function() {
  
  var
    
    handleRowSelectionStart = function(event) {
      
      var
        cartElement                 = $( Cart.elementId ) ,
        rowsElements                = Cart.findRowsElements() ,
        selectedRowsElements        = Cart.findSelectedRowsElements() ,
        // for selection range
        initiallySelectedRowElement = Cart.findInitiallySelectedRowElement() || this ,
        startingIndex               = rowsElements.indexOf(initiallySelectedRowElement) ,
        endingIndex                 = rowsElements.indexOf(this) ,
        rangeStartingIndex          = Math.min( startingIndex, endingIndex ) ,
        rangeEndingIndex            = Math.max( startingIndex, endingIndex ) ,
        selectionRange              = $R( rangeStartingIndex, rangeEndingIndex )
      ;
      
      // initial selection
      if ( !initiallySelectedRowElement.hasClassName('initially_selected') ) {
        initiallySelectedRowElement.addClassName('initially_selected') ;
      } if ( !event.shiftKey ) {
        rowsElements.invoke( 'removeClassName', 'initially_selected' ) ;
        this.addClassName('initially_selected') ;
      }
      
      // no modifier keys
      if ( !event.ctrlKey && !event.shiftKey ) {
        if ( !selectedRowsElements.include(this) ) {
          rowsElements.invoke( 'removeClassName', 'selected' ) ;
          this.addClassName('selected') ;
        }
      }
      
      // ctrl key
      if ( event.ctrlKey && !event.shiftKey ) {
        this.toggleClassName('selected') ;
      }
      
      // shift key
      if ( event.shiftKey ) {
        if ( !event.ctrlKey ) rowsElements.invoke( 'removeClassName', 'selected' ) ;
        selectionRange.each( function(index) {
          // rowsElements[index].toggleClassName('selected') ;
          if ( !rowsElements[index].hasClassName('selected') ) {
            rowsElements[index].addClassName('selected') ;
          }
        } ) ;
      }
      
    } ,
    
    handleRowSelectionEnd = function(event) {
      if ( event.ctrlKey || event.shiftKey ) {
        selection.collapse() ;
      }
    } ,
    
    handleRowDraggingUpdate = function( draggableInstance, event ) {
      var callbackArguments = [ event, [ event.pointerX(), event.pointerY() ] ] ;
      if ( !this._selectedRowsDraggingTrigger( draggableInstance, 'updateDrag', callbackArguments ) ) return ;
      // selection.collapse() ;
    } ,
    
    handleRowDraggingStart = function( draggableInstance, event ) {
      
      if ( !this._selectedRowsDraggingTrigger( draggableInstance, 'initDrag', [event] ) ) return ;
      if ( !this._selectedRowsDraggingTrigger( draggableInstance, 'startDrag', [event] ) ) return ;
      Draggables.activate(draggableInstance) ;
      // draggableInstance.initDrag(event) ;
      
      this.boundContextTipsUpdateHandler(event) ;
      document.observe( 'keydown', this.boundContextTipsUpdateHandler ) ;
      document.observe( 'keyup', this.boundContextTipsUpdateHandler ) ;
      
    } ,
    
    handleRowDraggingStop = function( draggableInstance, event ) {
      
      if ( !this._selectedRowsDraggingTrigger( draggableInstance, 'endDrag', [event] ) ) return ;
      
      this.boundContextTipsUpdateHandler( event, true ) ;
      document.stopObserving( 'keydown', this.boundContextTipsUpdateHandler ) ;
      document.stopObserving( 'keyup', this.boundContextTipsUpdateHandler ) ;
      
    } ,
    
    handleContextTipsUpdate = function(event) {
      if ( this._previousContextTipsUpdateHandlerEventType != event.type ) {
        this._previousContextTipsUpdateHandlerEventType = event.type ;
        this._handleContextTipsUpdate.apply( this, arguments ) ;
      }
    }
    
  ;
  
  return {
    
    findRowsElements: function() {
      // this.rowsElements = $( this.elementId ).select( '.row.selectable' ) ;
      return this.rowsElements ;
    } ,
    
    findSelectedRowsElements: function() {
      var rowsElements = this.findRowsElements() ;
      this.selectedRowsElements = rowsElements.findAll( function(r) { return r.hasClassName('selected') } ) ;
      return this.selectedRowsElements ;
    } ,
    
    findInitiallySelectedRowElement: function() {
      var rowsElements = this.findRowsElements() ;
      this.initiallySelectedRowElement = rowsElements.find( function(r) { return r.hasClassName('initially_selected') } ) ;
      return this.initiallySelectedRowElement ;
    } ,
    
    makeRowSelectable: function(rowElementOrId) {
      rowElement = $(rowElementOrId) ;
      if ( this.rowsElements.any(function(x){ return x == rowElement }) ) return ;
      this.rowsElements.push(rowElement) ;
      rowElement.observe( 'mousedown', this.rowSelectionStartHandler ) ;
      rowElement.observe( 'mouseup', this.rowSelectionEndHandler ) ;
    } ,
    
    makeRowUnselectable: function(rowElementOrId) {
      rowElement = $(rowElementOrId) ;
      if ( !rowElement ) return ;
      if ( !this.rowsElements.any(function(x){ return x == rowElement }) ) return ;
      this.rowsElements = this.rowsElements.reject( function(x) { return x == rowElement } ) ;
      rowElement.stopObserving( 'mousedown', this.rowSelectionStartHandler ) ;
      rowElement.stopObserving( 'mouseup', this.rowSelectionEndHandler ) ;
    } ,
    
    makeRowDraggable: function(elementId) {
      new Draggable( elementId, {
        
        ghosting: true ,
        revert: true ,
        reverteffect: function( element, topOffset, leftOffset ) {
          var duration = Math.sqrt( Math.abs(topOffset^2) + Math.abs(leftOffset^2) ) * 0.02 ;
          new Effect.Move( element, { x: -leftOffset, y: -topOffset, duration: duration ,
            queue: { scope: '_draggable' } // , position: 'with-last'
          } ) ;
        } ,
        
        onStart: this.boundRowDraggingStartHandler ,
        onEnd: this.boundRowDraggingStopHandler ,
        onDrag: this.boundRowDraggingUpdateHandler
        
      } ) ;
    } ,
    
    makeTrashDroppable: function(options) {
      
      options = $H(options) ;
      var
        url                  = options.unset('url') ,
        ajaxOptions          = $H( options.unset('ajaxOptions') ) ,
        requestDefaultParams = ajaxOptions.unset('parameters')
      ;
      
      Droppables.add( 'Trash', {
        accept: 'draggable_element' ,
        hoverclass: 'hover' ,
        onDrop: function( draggableElement, droppableElement, event ) {
          
          if ( Cart.findInitiallySelectedRowElement() != draggableElement ) return ;
          
          var
            selectedRowsElements = Cart.findSelectedRowsElements() ,
            requestIdsParam      = selectedRowsElements.inject( [], function( array, rowElement ) {
                array.push( rowElement.id.split('_').last() ) ;
                return array ;
              } ) ,
            requestParams        = 'id=' + requestIdsParam.join(',') + '&' + requestDefaultParams
          ;
          if ( event.shiftKey ) requestParams += '&completely' ;
          
          ajax_before() ;
          
          new Ajax.Request( url, $H({
            parameters: requestParams
          }).merge(ajaxOptions).toObject() ) ;
          
        }
      } ) ;
      
    } ,
    
      _previousContextTipsUpdateHandlerEventType: '' ,
      
      _handleContextTipsUpdate: function( event, theEnd ) {
        
        var contextTipElement = $('CartContextTip') ;
        if (theEnd) contextTipElement.update() ;
        
        this.findSelectedRowsElements().pluck('id').each( function(id) {
          
          var
            draggableInstance      = Draggables.drags.find( function(d) { return d.element.id == id } ) ,
            draggableElement       = draggableInstance.element ,
            currentQuantityElement = draggableElement.down('.current_quantity') ,
            minusQuantityElement   = draggableElement.down('.minus_quantity') ,
            currentQuantity        = currentQuantityElement.innerHTML ,
            minusQuantity          = 1 ,
            contextTip             = ''
          ;
          
          if (theEnd) {
            minusQuantityElement.update().hide() ;
            return ;
          }
          
          if ( event.shiftKey ) {
            contextTip = 'Remove entr[y|ies] completely' ;
            minusQuantity = currentQuantity ;
          }
          
          contextTipElement.update(contextTip) ;
          minusQuantityElement.update( '- ' + minusQuantity ).show() ;
          
        } ) ;
        
      } ,
      
      _selectedRowsDraggingTrigger: function( currentDraggableInstance, callbackFunction, callbackArguments ) {
        
        var initiallySelectedRowElement = this.findInitiallySelectedRowElement() ;
        if ( initiallySelectedRowElement != currentDraggableInstance.element ) return false ;
        
        this.findSelectedRowsElements().pluck('id').each( function(id) {
          
          var selectedDraggableInstance = Draggables.drags.find( function(d) { return d.element.id == id } ) ;
          
          if ( selectedDraggableInstance.element != currentDraggableInstance.element ) {
            selectedDraggableInstance[callbackFunction].apply( selectedDraggableInstance, callbackArguments ) ;
          }
          
        } ) ;
        
        return true ;
        
      } ,
    
    boundContextTipsUpdateHandler: handleContextTipsUpdate.bind(this) ,
    boundRowDraggingStartHandler: handleRowDraggingStart.bind(this) ,
    boundRowDraggingStopHandler: handleRowDraggingStop.bind(this) ,
    boundRowDraggingUpdateHandler: handleRowDraggingUpdate.bind(this) ,
    rowSelectionStartHandler: handleRowSelectionStart ,
    rowSelectionEndHandler: handleRowSelectionEnd ,
    
    elementId: null ,
    rowsElements: []
    
  }
  
}.apply(Cart) ) ;

function manage_cart ( big_change, element_id ) {
  
  SHOULD_UPDATE_WINDOW_SIZE = false ;
  
  if (big_change) update_by_proxy_with_effect(element_id) ;
  else _update_with_proxy(element_id) ;
  
}

//
// Animation specific functions
//

function update_by_proxy_with_effect(id) {
  
  var proxy_id = proxify_id(id) ;
  
  $(id).makeClipping() ;
  
  new Effect.Fade( id, {
    to: 0.01 ,
    duration: 0.5 ,
    afterFinish: function(effect) {
      
      var needed_height   = $(proxy_id).getHeight() ;
      var existing_height = $(id).getHeight() ;
      var percent         = ( needed_height / existing_height ) * 100 ;
      
      new Effect.Scale( id, percent, {
        scaleContent: false ,
        scaleX: false ,
        scaleY: true ,
        duration: 0.5 ,
        afterFinish: function(effect) {
          
          _update_with_proxy(id) ;
          new Effect.Appear( id, {
            duration: 0.5 ,
            afterFinish: function() {
              $(id).undoClipping() ;
              $(id).setStyle({ height: 'auto' }) ;
              _highlightCurrentElements() ;
              try_to_focus_first_form_element_within( id, 0 ) ;
            }
          }) ;
          
        }
      }) ;
      
    }
  }) ;
  
}
  
  function _update_with_proxy(id) {
    $(id).update() ;
    $( proxify_id(id) ).childElements().each( function(i) {
      $(id).insert(i) ;
    } ) ;
    _highlightCurrentElements() ;
  }
  
  function _highlightCurrentElements() {
    if ( !$( Cart.elementId ) ) return ;
    $( Cart.elementId ).select('.current_cart_item').each( function(x) {
      // x.removeClassName('current_cart_item') ;
      x.pulsate({ duration:0.5 }) ;
    }) ;
  }

//
// Ajax specific functions
//

function ajax_before () {
  // console.log('before') ;
  SHOULD_TRY_TO_FOCUS_FIRST_FORM_ELEMENT = true ;
  _set_cursor('wait') ;
}

function ajax_success (xhr) {
  // console.log( 'success', xhr ) ;
  _set_cursor('') ;
}

function ajax_failure (xhr) {
  // console.log( 'failure', xhr ) ;
  _set_cursor('') ;
  alert( 'Oops, an error has occured. Please try again!' ) ;
}

function ajax_complete (xhr) {
  // console.log( 'complete', xhr ) ;
  return ajax_status_check( xhr, function(xhr) {
    
    var
      update_container = xhr.request.container ,
      update_container_id = null ,
      unproxyfied_id = null
    ;
    
    if ( !update_container || !( update_container_id = update_container.success ) ) return ;
    
    _add_notice_error_observers(update_container_id) ;
    
    if ( unproxyfied_id = unproxify_id(update_container_id) ) update_by_proxy_with_effect(unproxyfied_id) ;
    else try_to_focus_first_form_element_within(update_container_id) ;
    
  } ) ;
}

function ajax_status_check( xhr, callback ) {
  if (!xhr) return false ;
  var status = xhr.getResponseHeader('Status') ;
  if ( status && status.match(/^(2\d{2}|304)/) && !xhr.responseText.empty() ) {
    ajax_success(xhr) ;
    callback(xhr) ;
    return true ;
  } else {
    ajax_failure(xhr) ;
    return false ;
  }
}
  
  function _set_cursor(style) {
    document.documentElement.className = style ;
  }

//
// Notice/error bars specific functions
//

function close_notice_error_bar( self, class_name ) {
  
  var bar = $(self).up('.'+class_name) ;
  bar.stopObserving( 'mouseover', _notice_error_mouseover ) ;
  bar.stopObserving( 'mouseout', _notice_error_mouseout ) ;
  bar.fade({
    duration: 0.5 ,
    afterFinish: function(effect) {
      var win ;
      if ( win = Windows.getFocusedWindow() ) update_window_size() ;
    }
  }) ;
  
}
  
  function _add_notice_error_observers(container_id) {
    $(container_id).select( '.notice', '.error' ).each( function(i) {
      i.observe( 'mouseover', function(e) { _notice_error_mouseover( e, i ) } ) ;
      i.observe( 'mouseout',  function(e) { _notice_error_mouseout( e, i ) } ) ;
    } ) ;
  }
  
  function _notice_error_mouseover( event, container ) {
    if ( !_check_notice_error_related_target( event, container ) ) {
      container.down('.close_button').show() ;
    }
  }
  
  function _notice_error_mouseout( event, container ) {
    if ( !_check_notice_error_related_target( event, container ) ) {
      container.down('.close_button').hide() ;
    }
  }
  
  function _check_notice_error_related_target( event, container ) {
    if ( container.descendants().include( event.relatedTarget ) ) {
      return true ;
    } else if ( container == event.relatedTarget ) {
      return true ;
    } else {
      return false ;
    }
  }

//
// Tool Tip functions
//

function updateTooltipContext( updateMethod, options ) {
  
  if ( !Object.isElement(this) ) {
    try {
      // arguments.callee.name
      throw 'updateTooltipContext: this property must be an HTMLElement' ;
    } catch(error) {
      return alert(error) ;
    }
  }
  
  if (!updateMethod) updateMethod = 'insert' ;
  if (!options) options = {} ;
  if ( !options.newContext ) {
    options.newContext = this.select('[title]') ;
    if ( this.hasAttribute('title') ) options.newContext = options.newContext.concat( [this] ) ;
  }
  if ( !Object.isArray(options.newContext) ) options.newContext = [options.newContext] ;
  if ( !options.tooltipObject ) options.tooltipObject = MAIN_TOOLTIP ;
  if ( Object.isUndefined(options.replaceExisting) ) options.replaceExisting = true ;
  
  var newContext = options.newContext ;
  var tooltipObject = options.tooltipObject ;
  var existingContext = tooltipObject.cfg.getProperty('context') ;
  var updatedContext = existingContext || [] ;
  
  newContext.each( function(contextItem) {
    var index = this.indexOf(contextItem) ,
      included = ( index != -1 )
    ;
    switch(updateMethod) {
      
      case 'insert':
        if (included) {
          if ( options.replaceExisting ) {
            this.splice( index, 1, contextItem ) ;
          } else {
            return ;
          }
        } else {
          this.push(contextItem) ;
        }
        break ;
      
      case 'remove':
        if (!included) return ;
        this.splice( index, 1 ) ;
        break ;
      
    }
  }, updatedContext ) ;
  
  return tooltipObject.cfg.setProperty( 'context', updatedContext ) ;
  
}

//
// In Place Editor functions
//

function createInPlaceEditor( id, url, options ) {
  
  if (!url) url = '' ;
  if (!options) options = {} ; options = $H(options) ;
  var
    instance ,
    type     = [ options.unset('type') ].flatten() ,
    title    = options.unset('title') || '' ,
    endColor = $(id).up('.row').getStyle('background-color') ,
    rgbColor = []
  ;
  if ( endColor.match(/rgb/i) ) {
    endColor.scan( /(\d+)/, function(m) { rgbColor.push( parseInt( m[0], 10 ) ) } ) ;
    endColor = '#' + rgbColor.invoke('toColorPart').join('') ;
    // YAHOO.util.Color.rgb2hex(rgbColor)
  }
  
  instance = new Ajax.InPlaceEditor( id, url, $H({
    
    okControl: 'link' ,
    htmlResponse: false ,
    highlightendcolor: endColor ,
    onComplete: ajax_complete ,
    onFailure: function() {} ,
    
    onLeaveEditMode: function(objectInstance) {
      destroyPanel( objectInstance.panelInstance ) ;
    } ,
    
    onFormCustomization: function( objectInstance, formElement ) {
      ( function() {
        var panelInstance = createPanelForInPlaceEditor(objectInstance) ;
        objectInstance.panelInstance = panelInstance ;
      } ).defer() ;
    }
    
  }).merge(options).toObject() ) ;
  
  instance.options.callback = function( form, value ) {
    ajax_before() ;
    destroyPanel( this.panelInstance ) ;
    var params = Form.serialize(form) ;
    return params + '&' + this.options.ajaxOptions.parameters ;
  }.bind(instance) ;
  
  instance.type  = type ;
  instance.title = title ;
  
  IPE_INSTANCES.set( id, instance ) ;
  return instance ;
  
}

function destroyInPlaceEditor(instanceOrElement) {
  
  var instance = element = instanceOrElement ;
  
  if ( Object.isElement(element) ) {
    if ( instance = IPE_INSTANCES.find(function(pair){ return pair.value.element == element }) ) {
      instance = instance.value ;
    } else {
      return ;
    }
  }
  
  if ( ipeDestroyed(instance) ) return ;
  
  IPE_INSTANCES.unset(instance.element.id) ;
  instance.destroy() ;
  
}

function ipeDestroyed(instance) {
  return !instance ;
}

function ipeExternalControl( controlElementId, ipeElementId ) {
  $(controlElementId).observe( 'dblclick', function(event) {
    var ipeInstance = IPE_INSTANCES.get(ipeElementId) ;
    if ( !ipeDestroyed(ipeInstance) && !ipeInstance._editing ) {
      ipeInstance.enterEditMode(event) ;
    }
  } ) ;
}

//
// Panel functions
//

function createPanelForInPlaceEditor(ipeInstance) {
  
  var
    panelInstance ,
    sourceElement   = ipeInstance.element ,
    formElement     = ipeInstance._form ,
    panelInstanceId = sourceElement.id
  ;
  
  sourceElement.makeHidden() ;
  
  panelInstance = createPanel({
    instanceId: panelInstanceId ,
    context: { elementOrId: sourceElement } ,
    body: formElement ,
    cssClass: 'just_bd' ,
    onClickOutsideElement: ipeInstance._boundCancelHandler
  }) ;
  
  panelInstance.destroyEvent.subscribe( function( eventType, args ) {
    sourceElement.makeVisible(false) ;
  } ) ;
  
  return panelInstance ;
  
}

function createErrorPanelForIPE( ipeInstance, errorText ) {
  
  var
    sourceElement   = ipeInstance.element ,
    editorElement   = ipeInstance._controls.editor ,
    panelInstanceId = sourceElement.id ,
    panelInstance
  ;
  
  panelInstance = createErrorPanel({
    instanceId: panelInstanceId + '_error' ,
    context: {
      elementOrId: editorElement ,
      overlayCorner: 'bl'
    } ,
    body: errorText ,
    cssClass: 'just_bd'
  }) ;
  
  return panelInstance ;
  
}

function createErrorPanel(options) {
  options = $H(options) ;
  return createPanel( $H({
    cssClass: ['error'].concat([ options.unset('cssClass') ])
  }).merge(options) ) ;
}

function createPanel(options) {
  
  if (!options) options = {}; options = $H(options) ;
  var
    
    instanceId            = options.unset('instanceId') + '_panel' ,
    instance              = options.unset('instance') || new YAHOO.widget.Panel(
                            instanceId, {
                              appendtodocumentbody: true ,
                              visible: false
                            } ) ,
    
    context               = $H({
                              elementOrId: DEFAULT ,
                              overlayCorner: 'tl' ,
                              contextCorner: 'tl' ,
                              triggerEvents: [ 'beforeShow', 'windowResize' ]
                            }).merge( options.unset('context') ) ,
    contextArray          = context.toArray().pluck('value') ,
    
    appendToNode          = options.unset('appendToNode') || document.body ,
    instanceElement       = $(instance.element) ,
    body                  = options.unset('body') ,
    cfg                   = $H( options.unset('cfg') || {} ) ,
    cssClassArray         = [ options.unset('cssClass') ].flatten() ,
    cssClass              = cssClassArray.uniq().join(' ') ,
    formElement ,
    onClickOutsideElement = options.unset('onClickOutsideElement')
    
  ;
  PANEL_INSTANCES.set( instanceId, instance ) ;
  
  instance.setBody(body) ;
  instance.render(appendToNode) ;
  options.each( function(option) {
    instance[ option.key ] = option.value ;
  } ) ;
  cfg.each( function(property) {
    instance.cfg.setProperty( property.key, property.value ) ;
  } ) ;
  instance.cfg.setProperty( 'context', contextArray ) ;
  instanceElement.addClassName(cssClass) ;
  instance.show() ;
  
  ( function() {
    formElement = instanceElement.down('form') ;
    if (formElement) formElement.focusFirstElement() ;
  } ).defer() ;
  
  ( function() {
    document.observe( 'click', function(event) {
      
      var
        event         = Event.extend(event) ,
        targetElement = event.element()
      ;
      
      if ( panelDestroyed(instance) ) return Event.stopThisObserver( event, this ) ;
      
      if ( Object.isElement(targetElement) ) {
        if ( targetElement != instanceElement && !targetElement.ancestors().include(instanceElement) ) {
          
          Event.stopThisObserver( event, this ) ;
          
          if (onClickOutsideElement) {
            onClickOutsideElement(event) ;
          } else {
            destroyPanel(instance) ;
          }
          
        }
      }
      
    } ) ;
  } ).defer() ;
  
  return instance ;
  
}

function destroyPanel(instance) {
  if ( panelDestroyed(instance) ) return ;
  PANEL_INSTANCES.unset(instance.id) ;
  instance.destroy() ;
}

function panelDestroyed(instance) {
  return !instance || !instance.cfg ;
}

//
// Auxiliary functions
//

function shouldProceedToNextPaymentStep(currentStep) {
  
  var
    result          = false ,
    verificationUrl = '/check_payment_procedure_step/' + currentStep
  ;
  
  new Ajax.Request( verificationUrl, {
    asynchronous: false ,
    method: 'get' ,
    onCreate: ajax_before ,
    onComplete: function( xhr, json ) {
        if ( ajax_complete(xhr) ) {
          if ( !json.successful ) createInfoDialog( json.errorText ) ;
          else result = true ;
        }
      } ,
    onException: function( reqIns, exc ) {
        console.log(exc) ;
      }
  } ) ;
  
  return result ;
  
}

function try_to_focus_first_form_element_within( container, msec ) {
  if (!SHOULD_TRY_TO_FOCUS_FIRST_FORM_ELEMENT) return ;
  setTimeout( function() {
      var form = $(container).down('form') ;
      if (form) {
        form.focusFirstElement() ;
        SHOULD_TRY_TO_FOCUS_FIRST_FORM_ELEMENT = false ;
      }
    }, ( msec !== undefined ? msec : 1000 )
  ) ;
}

function form_cancel_link(self) {
  var
    form = self.up('form') ,
    update_element_id = form.update_element.value ,
    content = form.secondary_link_content.value ,
    id
  ;
  $(update_element_id).update(content) ;
  if ( id = unproxify_id(update_element_id) ) update_by_proxy_with_effect(id) ;
}

function proxify_id(id) {
  if ( !unproxify_id(id) ) return id + 'Proxy' ;
}

function unproxify_id(id) {
  var proxy_re = /proxy$/i ;
  if ( id.match(proxy_re) ) {
    return id.sub( proxy_re, '' ) ;
  } else {
    return false ;
  }
}

function update_user_names() {
  if ( document.body ) {
    $( document.body ).select('.user_name').invoke( 'update', USER_NAME ) ;
  }
}

//
// Event functions
//

Element.beforeUpdate = function(content) {
  
  var
    elements = this.select('[id]') ,
    ipeInstancePair ,
    draggableInstance
  ;
  
  elements.each( function(element) {
    
    // remove from In Place Editor instances
    destroyInPlaceEditor(element) ;
    
    // remove form Draggable instances
    if ( draggableInstance = Draggables.drags.find( function(d) { return d.element == element } ) ) {
      draggableInstance.destroy() ;
    }
    
    // remove form Droppable instances
    Droppables.remove(element) ;
    
    // remove from Cart selectable rows
    Cart.makeRowUnselectable(element) ;
    
  }, this ) ;
  
  updateTooltipContext.bind( this, 'remove' )() ;
  
}

Element.afterInsert = function(insertions) {
  updateTooltipContext.bind(this)() ;
}

document.observe( 'dom:loaded', function(event) {
  
  var firstForm ;
  
  MAIN_TOOLTIP = new YAHOO.widget.Tooltip( 'MainTooltip', {
    appendtodocumentbody: true ,
    context: $$('[title]') // ,
    // showDelay: 100 ,
    // hideDelay: 0
  } ) ;
  
  // new Widgets.Filter( 'MainInner', {
  //   test: { label: 'Test key' } ,
  //   foo: { label: 'Foobar' }
  // } ) ;
  
  if ( firstForm = $( document.body ).down('form') ) {
    firstForm.focusFirstElement() ;
  }
  
} ) ;

