define([

'jquery'

], function ($) {

var Utils = {};

Utils.Extend = function (ChildClass, SuperClass) {
  var __hasProp = {}.hasOwnProperty;

  function BaseConstructor () {
    this.constructor = ChildClass;
  }

  for (var key in SuperClass) {
    if (__hasProp.call(SuperClass, key)) {
      ChildClass[key] = SuperClass[key];
    }
  }

  BaseConstructor.prototype = SuperClass.prototype;
  ChildClass.prototype = new BaseConstructor();
  ChildClass.__super__ = SuperClass.prototype;

  return ChildClass;
};

function getMethods (theClass) {
  var proto = theClass.prototype;

  var methods = [];

  for (var methodName in proto) {
    var m = proto[methodName];

    if (typeof m !== 'function') {
      continue;
    }

    if (methodName === 'constructor') {
      continue;
    }

    methods.push(methodName);
  }

  return methods;
}

Utils.Decorate = function (SuperClass, DecoratorClass) {
  var decoratedMethods = getMethods(DecoratorClass);
  var superMethods = getMethods(SuperClass);

  function DecoratedClass () {
    var unshift = Array.prototype.unshift;

    var argCount = DecoratorClass.prototype.constructor.length;

    var calledConstructor = SuperClass.prototype.constructor;

    if (argCount > 0) {
      unshift.call(arguments, SuperClass.prototype.constructor);

      calledConstructor = DecoratorClass.prototype.constructor;
    }

    calledConstructor.apply(this, arguments);
  }

  DecoratorClass.displayName = SuperClass.displayName;

  function ctr () {
    this.constructor = DecoratedClass;
  }

  DecoratedClass.prototype = new ctr();

  for (var m = 0; m < superMethods.length; m++) {
      var superMethod = superMethods[m];

      DecoratedClass.prototype[superMethod] =
        SuperClass.prototype[superMethod];
  }

  var calledMethod = function (methodName) {
    // Stub out the original method if it's not decorating an actual method
    var originalMethod = function () {};

    if (methodName in DecoratedClass.prototype) {
      originalMethod = DecoratedClass.prototype[methodName];
    }

    var decoratedMethod = DecoratorClass.prototype[methodName];

    return function () {
      var unshift = Array.prototype.unshift;

      unshift.call(arguments, originalMethod);

      return decoratedMethod.apply(this, arguments);
    };
  };

  for (var d = 0; d < decoratedMethods.length; d++) {
    var decoratedMethod = decoratedMethods[d];

    DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
  }

  return DecoratedClass;
};

var Observable = function () {
  this.listeners = {};
};

Observable.prototype.on = function (event, callback) {
  this.listeners = this.listeners || {};

  if (event in this.listeners) {
    this.listeners[event].push(callback);
  } else {
    this.listeners[event] = [callback];
  }
};

Observable.prototype.trigger = function (event) {
  var slice = Array.prototype.slice;
  var params = slice.call(arguments, 1);

  this.listeners = this.listeners || {};

  // Params should always come in as an array
  if (params == null) {
    params = [];
  }

  // If there are no arguments to the event, use a temporary object
  if (params.length === 0) {
    params.push({});
  }

  // Set the `_type` of the first object to the event
  params[0]._type = event;

  if (event in this.listeners) {
    this.invoke(this.listeners[event], slice.call(arguments, 1));
  }

  if ('*' in this.listeners) {
    this.invoke(this.listeners['*'], arguments);
  }
};

Observable.prototype.invoke = function (listeners, params) {
  for (var i = 0, len = listeners.length; i < len; i++) {
    listeners[i].apply(this, params);
  }
};

Utils.Observable = Observable;

Utils.generateChars = function (length) {
  var chars = '';

  for (var i = 0; i < length; i++) {
    var randomChar = Math.floor(Math.random() * 36);
    chars += randomChar.toString(36);
  }

  return chars;
};

Utils.bind = function (func, context) {
  return function () {
    func.apply(context, arguments);
  };
};

Utils._convertData = function (data) {
  for (var originalKey in data) {
    var keys = originalKey.split('-');

    var dataLevel = data;

    if (keys.length === 1) {
      continue;
    }

    for (var k = 0; k < keys.length; k++) {
      var key = keys[k];

      // Lowercase the first letter
      // By default, dash-separated becomes camelCase
      key = key.substring(0, 1).toLowerCase() + key.substring(1);

      if (!(key in dataLevel)) {
        dataLevel[key] = {};
      }

      if (k == keys.length - 1) {
        dataLevel[key] = data[originalKey];
      }

      dataLevel = dataLevel[key];
    }

    delete data[originalKey];
  }

  return data;
};

Utils.hasScroll = function (index, el) {
  // Adapted from the function created by @ShadowScripter
  // and adapted by @BillBarry on the Stack Exchange Code Review website.
  // The original code can be found at
  // http://codereview.stackexchange.com/q/13338
  // and was designed to be used with the Sizzle selector engine.

  var $el = $(el);
  var overflowX = el.style.overflowX;
  var overflowY = el.style.overflowY;

  //Check both x and y declarations
  if (overflowX === overflowY &&
      (overflowY === 'hidden' || overflowY === 'visible')) {
    return false;
  }

  if (overflowX === 'scroll' || overflowY === 'scroll') {
    return true;
  }

  return ($el.innerHeight() < el.scrollHeight ||
    $el.innerWidth() < el.scrollWidth);
};

Utils.escapeMarkup = function (markup) {
  var replaceMap = {
    '\\': '&#92;',
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    '\'': '&#39;',
    '/': '&#47;'
  };

  // Do not try to escape the markup if it's not a string
  if (typeof markup !== 'string') {
    return markup;
  }

  return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
    return replaceMap[match];
  });
};

// Append an array of jQuery nodes to a given element.
Utils.appendMany = function ($element, $nodes) {
  // jQuery 1.7.x does not support $.fn.append() with an array
  // Fall back to a jQuery object collection using $.fn.add()
  if ($.fn.jquery.substr(0, 3) === '1.7') {
    var $jqNodes = $();

    $.map($nodes, function (node) {
      $jqNodes = $jqNodes.add(node);
    });

    $nodes = $jqNodes;
  }

  $element.append($nodes);
};

return Utils;

});