/**
 * Misc. functions for Playground
 */
var PG = {
  
  extendObject: function(destination, source) {
    for(var property in source) {
      destination[property] = source[property];
    }
    return destination;
  },
    
  evalJSON: function(transport) {
    return eval("(" + transport + ")");
  },
  
  /**
   * Initializes javascript for playground
   * @param baseurl The URL to the project root
   */
  init: function(baseurl) {
    alert("PG.init (in playground.js) is no longer needed. Please remove this call.");
    this._baseURL = baseurl;
  },
  
  _getBaseUrl: function() {
    if(PG._baseURL) return this._baseURL;
    
    // This is the first time we are called. Retreive our base url. If we are running as a packed
    // javascript file, our current location is /serverpath/PROJECT/pack/MD5.js. Otherwise, our 
    // current location is /serverpath/PROJECT/basicweb/web/js/playground.js
    // 
    // There is no way to determine where our script is located from the script itself. However, 
    // we can look at our include path for the <script> tag in the HTML document.
    // The code below is stolen from scriptaculous.js
    var f = jQuery.grep(jQuery.makeArray(document.getElementsByTagName("script")), function(s) {
      return (s.src && s.src.match(/(basicweb\/web\/js\/playground|pack\/[\w\d]{32})\.js$/));
    });
    $(f).each(function(i, s) {
      var src = s.src;
      
      // If we are running Firefox, src will contain http://domainname. If we are running explorer,
      // it will *not*.
      if(PG.urlIsLocal(src)) src = "http://" + document.domain + src;
    
      if(src.match(/pack\/[\w\d]{32}\.js$/)) {
        // We are packed
        var urlparts = src.split("/");
        var baseurlparts = urlparts.slice(0, urlparts.length-2);
        PG._baseURL = baseurlparts.join("/");
      } else {
        // We are in basicweb
        var urlparts = src.split("/");
        var baseurlparts = urlparts.slice(0, urlparts.length-4);
        PG._baseURL = baseurlparts.join("/");
      }
    });
    return PG._baseURL;
  },
  
  /**
   * Returns wether a URL is local
   * 
   * A local URL is a URL which exists on the same http domain as the website.
   * If $url is an absolute path (starting with http://, for example
   * http://www.google.com) it is not local. 
   * @return bool
   */
  urlIsLocal: function(url) {
    return !url.match(/^http:\/\//i);
  },
  
  /**
   * Javascript equivalent of HTML::url
   */
  url: function(target) {
    if(!this.urlIsLocal(target)) return target;
  
    var url = this._getBaseUrl() + '/' + target;
    
    // Strip all double /
    // Start by removing the http:// so that it does not become http://
    url = url.substr(7);
    url = "http://" + url.replace(/\/{2,}/g, '/');
    
    return url;
  },
  
  /**
   * Returns the string after a #-sign in a url. This is useful in combination with jQuery, for 
   * instance id's can be stored after the #-sign, and be used as parameters for json requests 
   * later.
   * For instance:
   *   PG.afterHash('#234'); // Returns "234"
   *   PG.afterHash('othertext?params=values#hash'); // Returns "hash"
   *   PG.afterHash('no_hash_here'); // Returns ""
   *   PG.afterHash('string#hash#morehash'); // Returns "hash#morehash"
   * 
   * Usage example with jQuery:
   *   HTML:
   *   <a href="#2" class="updatebutton">Do something with this item</a>
   *   <a href="#4" class="updatebutton">Do something with this item</a>
   *   
   *   $('a.updatebutton').click(function() {
   *     var id = PG.afterHash($(this).attr('href'));
   *     
   *     $.get('/dosomething', {id: id}, function() {
   *       alert('did something!');
   *     });
   *   });
   * @return string
   */
  afterHash: function(url) {
    var hashPlace = url.indexOf('#');
    if(hashPlace == -1) return "";
    
    return url.substring(hashPlace+1);
  },
  
  /**
   * Shorthand version of document.location = url. This function exists since AbstractJavascript
   * can't handle assignments.
   * @param string url The new location to send the user to.
   */
  goto: function(url) {
    document.location = url;
  },
  /**
   * A wrapper for console.log.
   * IE does not have a console and that makes any call to console.log invalid.
   * This function will check that the console object exists before calling it.
   * 
   * You can send one or two parameters. ex;
   * PG.debug(foo); // will print the foo object to the console
   * PG.debug('The foo object is %o', foo); // will print "The foo object is" as label and the the foo object.
   */
  debug: function(lbl, obj) {
    if(window.console && window.console.log) {
      if(obj == undefined) // only the object
        window.console.log(lbl);
      else // with label
        window.console.log(lbl, obj);
    }
  },
  /**
   * Reloads the page
   */
  reload: function() {
    window.location.reload();
  },
  /**
   * Shows/hides the busy/loading indicator
   * @param boolean $show If true it will show the indicator, if false it will hide it.
   *                      If not provided (undefined) It will show the indicator
   */
  busy: function(show) {
    (show || show === undefined ? $('#pgbusy').show() : $('#pgbusy').hide());
  },
  /**
   * Returns true if the array contains the needle
   * Note. Can only be used on strings, ints etc.
   */
  in_array: function(haystack, needle) {
    for(var i in haystack) {
      if(haystack[i] === needle) return true;
    }
    return false;
  },
  /**
   * Internal help function for error/notice/success functions 
   */
  _showMessage: function(type, message, options) {
    var opts = {};
    var defaultOptions = {
      duration: null,
      closeBtn: true
    };
    $.extend(opts, defaultOptions, options);
    var boxid = 'l_' + type;
    var box = $('#' + boxid);
    if(!box.exists()) {
      box = $('<div id="'+ boxid +'" class="' + type + '"><div class="message"></div></div>').prependTo('#l_contentContainer');
    }
    if(opts.closeBtn) {
      if(box.children('span.pointer').size() === 0) {
        box.prepend($('<span class="right pointer">x</span>').click(function() {
          $(this).parents('#' + boxid).remove();
        }));
      }
    } else {
      box.children('span.pointer').remove();
    }
    box.children('div.message').html(message).show();
    $.scrollTo('0px');
    if(opts.duration) {
      $.timer(opts.duration, function(timer) {
        timer.stop();
        box.fadeOut(function() { $(this).remove(); });
      });
    }
    return box;
  },
  /**
   * These three functions will show an box with error/notice or success message.
   * Note that for these function to properly function there has to be a content container with
   * the id 'l_contentContainer' and the classes 'error', 'notice', 'success' has to be styled.
   * @param string $message The message you want to show the user
   * @param object $options Valid options are:
   *                        duration - The duration (ms) the box should be visible. Default is null (disabled).
   *                        closeBtn - True/false. If true the box will have a close button. Default true.
   */
  error: function(message, options) {
    this._showMessage('error', message, options);
  },
  notice: function(message, options) {
    this._showMessage('notice', message, options);
  },
  success: function(message, options) {
    this._showMessage('success', message, options);
  },
  /**
   * Default callback when a ajaxrequest fails. This is used by both postJSON and AjaxForms
   * @param object $data Raw XMLHttpRequest data from the server.
   */
  defaultErrorCallback: function(data) {
    alert("Error when communication with server");
  },
  /**
   * Default exception callback
   * @param object $respone JSON response. The exception message is stored in response.exception
   */
  defaultExceptionCallback: function(response) {
    alert(response.exception);
  },
  /**
   * Default success callback
   * @param object $response JSON response.
   */
  defaultSuccessCallback: function(response) {
  }
};

PG.Ajax = {
  /**
   * Creates an ajax-request to the ajax action located at @p url. If the response is 
   * successfull, @p onSuccess will be executed. If an exception is thrown by the action, it
   * will be presented to the end user
   * @param string url The action to call
   * @param function onSuccess The javascript function to run if all went ok. Will be passed
   *                           a parameter containg the json response as an object.
   * @return bool This function always returns false. It allows the use of it as the onsubmit
   *              attribute for forms.
   */
  onSuccess: function(url, onSuccess) {
    $.ajax({
      url: url,
      type: 'POST',
      success: function(j, s) {
        var json = PG.evalJSON(j);
        if(json['exception']) return alert(json['exception']);
        onSuccess(json);
      }
    });
    return false;
  }
};

/**
 * Returns the items "object" id.
 * For example if you have an DOM element with the id "user-12" it will return "12"
 * Note that your "object" id MUST be the last part of the string, eg 12-user will return 'user'
 * @param string $seperator If not specified it will use '-' as the seperator.
 * @return string 
 */
jQuery.fn.idFragment = function(seperator) {
  try {
    if(seperator === undefined) seperator = '-';
    var parts = this.attr('id').split(seperator);
    return parts[parts.length-1];
  } catch (e) {
    return '';
  }
};

/**
 * Returns the first parent of the element which is of a specific type. For example, if we have
 * a form field, we could execute Element.getFirstParent(formField, 'form') to get the form that
 * the element is located in.
 * @param string elementType The type of element to find
 * @return jQuery object
 */
jQuery.fn.getFirstParent = function(elementType) {
  return this.parents(elementType + ':first');
};

/**
 * Sets attribute disabled='disabled'
 */
jQuery.fn.disableInput = function() {
  this.each(function() {
    $(this).attr('disabled', 'disabled');
  });
  return this;
};

/**
 * Removes the 'disabled' attribute
 */
jQuery.fn.enableInput = function() {
  this.each(function() {
    $(this).removeAttr('disabled');
  });
  return this;
};

/**
 * Toggles the 'disabled' attribute
 */
jQuery.fn.toggleDisabled = function() {
  this.each(function() {
    if($(this).attr('disabled') == 'disabled')
      $(this).enableInput();
    else
      $(this).disableInput();
  });
  return this;
};

/**
 * Checks if a element exists or not
 */
jQuery.fn.exists = function() {
  return ($(this).length > 0);
};

/**
 * Check if the value of a element is empty or not
 * The value will be trimmed when checking
 */
jQuery.fn.isEmpty = function() {
  return !($.trim($(this).val()).length > 0);
};

String.prototype.strip = function() {
  return this.replace(/^\s+/, '').replace(/\s+$/, '');
};

/**
 * Makes an AJAX post request and interprets the response as JSON.
 * @param string url The URL to make the request to
 * @param data Map Key/value pairs that will be sent to the server.
 * @param callback Function|Map The callback to execute when a response is received. It may either 
 *                              be specified as a single callback, which will then be called on 
 *                              success, or as a map. If specified as a map, three callbacks may
 *                              be defined, exception, error and success. 
 *                              * exception will be executed if the pagecontroller action throws an
 *                                exception. The actual exception will then be stored in
 *                                data['exception'].
 *                              * error will be executed if there was communication errors with the
 *                                action. data will in this case contain the raw html response.
 *                              * success will be executed if everything went allright, and data 
 *                                will contain the JSON response.
 *                              All actions except success are optional. The default action for 
 *                              exception and error is to present the errors to the user using
 *                              alert().
 */
jQuery.extend({postJSON: function(url, data, callback) {
  var _callbacks = callback;

  // Find out if callback is an array or a single callback
  if(callback instanceof Function) {
    _callbacks = { success: callback };
  }
  
  // Initialize callbacks if they are not available
  if(_callbacks == undefined) _callbacks = [];
  
  if(_callbacks['success'] == undefined) {
    _callbacks['success'] = PG.defaultSuccessCallback;
  }
  
  if(_callbacks['exception'] == undefined) {
    _callbacks['exception'] = PG.defaultExceptionCallback;
  }
  
  if(_callbacks['error'] == undefined) {
    _callbacks['error'] = PG.defaultErrorCallback;
  }
  
  // Do the request
  $.ajax({
    type: 'POST',
    url: url,
    data: data,
    success: function(data, textStatus) {
      var json = PG.evalJSON(data);
      if(json['exception'])
        _callbacks['exception'](json);
      else
        _callbacks['success'](json);
    },
    error: function(data, textStatus) {
      _callbacks['error'](data, textStatus);
    }
  });
}});

// Initialize jQuery so all ajax-requests are made using POST by default.
$(function() {
  $.ajaxSetup({ type: 'POST' });
});
