(function(API){

function Input(val){
  return inputFactory(ret, this, val, this.description);
}

function inputFactory(type, parent, label, description){
  if (type === 'input') {
    var ret = function input(val){ return Input.call(ret, val) };
  } else {
    var ret = function noInput(){ return NoInput.call(ret) }
  }
  Object.keys(parent).forEach(function(s){
    fn[s] = inputFactory(this[s].name, fn, s, this[s].description);
  }, parent);
  return fn.define({
    _description: description,
    _parent: parent,
    _label: label,
    get path(){ return this.parent.path + '/' + this.label }
  });
}

  var ret = function(){ return noInput.call(ret) };
function noInput(){
  console.log(this.path);
}

API = API.reduce(function(r, s){
  var parts = s.path.split('/'), current = [];
  [].concat(parts).reduce(function(rr, ss){
    current.push(parts.shift());
    ss = ss.replace('-', '');
    var ret;
    if (ss[0] === '{' && ss[ss.length-1] === '}') {
      ss = ss.slice(1, -1);
      ret = function(val){ return input.call(ret, val) }
    } else {
      ret = function(){ return noInput.call(ret) }
    }
    ret.define({
      _description: s.description,
      _parent: rr,
      _label: ss,
      get path(){ return this.parent.path + '/' + this.label }
    });
    if (ss in rr) return rr[ss];
    return rr = rr[ss] = ret;
  }, r);
  return r;
}, Object.create(null, {path:{value:''}}));


function PathHandler(options){
  this.base = options.base;
  this.jsonp = options.jsonp || null;
}

PathHandler.prototype.define({
  path: accessor
  ( function( p ){ return p.join('/') },
    function(p,v){
    p = p || [];
    if (v === null) {
      this.last = [].concat(p);
      return [];
    } else if (typeof v === 'string') {
      v = v.split('/');
    }
    if (Array.isArray(v)) {
      p.push.apply(p, v);
    }
    return p;
  } ),

  jsonp: accessor
  ( function( p ){ return p ? '?' + p + '=?' : '' },
    function(p,v){ return v || p || '' } ),

  save: function(name){
    this.path = null;
    this.define(name, accessor
    ( function(p  ){ return this.resolve(p.join('/')) },
      function(p,v){ return p === undefined ? v : p } ));
    this[name] = this.last;
    delete this.last;
  },

  toString: function(){ return this.resolve().href },

  _resolve: (function(a){
    return function(path){
      a.href = this.base +'/' + (path ? path : this.path) + this.jsonp;
      return {
        host: a.host,
        href: a.href,
        path: a.pathname,
        search: a.search,
        port: a.port || a.protocol === 'https:' ? 443 : 80,
        protocol: a.protocol,
        toString: function(){ return this.href }
      };
    }
  })(document.createElement('a'))
});




function APIClient(options){
  this.paths = new PathHandler(options);
  this.jsonp = options.jsonp;
}

APIClient.prototype.define({
  paths: accessor
  ( function( p ){ return p },
    function(p,v){
      if (!p) return v;
      p.path = null;
      p.path = v;
      return p;
    } ),
  handler: accessor
  ( function(){
      var self = this;
      return function(d, s, xhr){ self.data = d }; } ),
  load: function(label, path){
    var url = this.paths[label] || (this.paths[label] = path);
    $.ajax({
      url: url+'',
      dataType: this.jsonp ? 'jsonp' : 'json',
      success: this.handler,
      jsonp: this.jsonp
    });
  }
});

function StackOverflowClient(){
  APIClient.call(this, {
    base: 'http://api.stackoverflow.com/1.1',
    jsonp: 'jsonp'
  });
}

StackOverflowClient.prototype = Object.create(APIClient.prototype);

var stacko = new StackOverflowClient;
//stacko.paths.profile = ['users', 748221];
//stacko.path = ['users', 748221];



function accessor(get, set){
  return set ? { get: get, set: set } : { get: get };
}

// inverting the order of the source from execution to move boilerplate to the bottom
})([{
  path: "answers",
  description: "Get all answers on the site."
},{
  path: "answers/{ids}",
  description: "Get answers identified by a set of ids."
},{
  path: "answers/{ids}/comments",
  description: "Get comments on the answers identified by a set of ids."
},{
  path: "badges",
  description: "Get all badges on the site, in alphabetical order."
},{
  path: "badges/{ids}",
  description: "Get all users who have been awarded the badges identified by ids."
},{
  path: "badges/name",
  description: "Get all non-tagged-based badges in alphabetical order."
},{
  path: "badges/tags",
  description: "Get all tagged-based badges in alphabetical order."
},{
  path: "comments",
  description: "Get all comments on the site."
},{
  path: "comments/{ids}",
  description: "Get comments identified by a set of ids."
},{
  path: "posts/{ids}/comments",
  description: "Get comments on the posts (question or answer) identified by a set of ids."
},{
  path: "errors/{id}",
  description: "Simulate an API error for testing purposes."
},{
  path: "privileges",
  description: "Get all the privileges available on the site."
},{
  path: "questions",
  description: "Get all questions on the site."
},{
  path: "questions/{ids}",
  description: "Get the questions identified by a set of ids."
},{
  path: "questions/{ids}/answers",
  description: "Get the answers to the questions identified by a set of ids."
},{
  path: "questions/{ids}/comments",
  description: "Get the comments on the questions identified by a set of ids."
},{
  path: "questions/{ids}/linked",
  description: "Get the questions that link to the questions identified by a set of ids."
},{
  path: "questions/{ids}/related",
  description: "Get the questions that are related to the questions identified by a set of ids."
},{
  path: "questions/{ids}/timeline",
  description: "Get the timelines of the questions identified by a set of ids."
},{
  path: "questions/unanswered",
  description: "Get all questions the site considers unanswered."
},{
  path: "questions/no-answers",
  description: "Get all questions on the site with no answers."
},{
  path: "revisions/{ids}",
  description: "Get all revisions for posts identified by a set of ids."
},{
  path: "revisions/{id}/{revision-guid}",
  description: "Get a specific revision identified by a post id and a revision guid."
},{
  path: "search",
  description: "Search the site for questions meeting certain criteria."
},{
  path: "similar",
  description: "Search the site based on similarity to a title."
},{
  path: "stats",
  description: "Get a set of statistics for the entire site."
},{
  path: "tags",
  description: "Get the tags on the site."
},{
  path: "tags/synonyms",
  description: "Get all the tag synonyms on the site."
},{
  path: "tags/{tags}/synonyms",
  description: "Get the synonyms for a specific set of tags."
},{
  path: "tags/{tags}/wikis",
  description: "Get the wiki entries for a set of tags."
},{
  path: "tags/{tag}/top-answerers/{period}",
  description: "Get the top answer posters in a specific tag, either in the last month or for all time."
},{
  path: "tags/{tag}/top-askers/{period}",
  description: "Get the top question askers in a specific tag, either in the last month or for all time."
},{
  path: "users",
  description: "Get all users on the site."
},{
  path: "users/{ids}",
  description: "Get the users identified by a set of ids."
},{
  path: "users/{ids}/answers",
  description: "Get the answers posted by the users identified by a set of ids."
},{
  path: "users/{ids}/badges",
  description: "Get the badges earned by the users identified by a set of ids."
},{
  path: "users/{ids}/comments",
  description: "Get the comments posted by the users identified by a set of ids."
},{
  path: "users/{ids}/comments/{to-id}",
  description: "Get the comments posted by a set of users in reply to another user."
},{
  path: "users/{ids}/favorites",
  description: "Get the questions favorited by users identified by a set of ids."
},{
  path: "users/{ids}/mentioned",
  description: "Get the comments that mention one of the users identified by a set of ids."
},{
  path: "users/{ids}/questions",
  description: "Get the questions asked by the users identified by a set of ids."
},{
  path: "users/{ids}/questions/no-answers",
  description: "Get the questions asked by a set of users, which have no answers."
},{
  path: "users/{ids}/questions/unaccepted",
  description: "Get the questions asked by a set of users, which have at least one answer but no accepted answer."
},{
  path: "users/{ids}/questions/unanswered",
  description: "Get the questions asked by a set of users, which are not considered to be adequately answered."
},{
  path: "users/{ids}/reputation",
  description: "Get a subset of the reputation changes experienced by the users identified by a set of ids."
},{
  path: "users/{ids}/tags",
  description: "Get the tags that the users (identified by a set of ids) have been active in."
},{
  path: "users/{id}/tags/{tags}/top-answers",
  description: "get the top answers a user has posted on questions with a set of tags."
},{
  path: "users/{id}/tags/{tags}/top-questions",
  description: "get the top questions a user has posted on questions with a set of tags."
},{
  path: "users/{ids}/timeline",
  description: "Get a subset of the actions of that have been taken by the users identified by a set of ids."
},{
  path: "users/{id}/top-answer-tags",
  description: "Get the top 30 tags (by score) a single user has posted answers in."
},{
  path: "users/{id}/top-question-tags",
  description: "Get the top 30 tags (by score) a single user has asked questions in."
},{
  path: "users/moderators",
  description: "Get the users who have moderation powers on the site."
},{
  path: "sites",
  description: "Get all the sites in the Stack Exchange network."
},{
  path: "users/{id}/associated",
  description: "Get a user's associated accounts."
}],
// prevent overwriting if desired
'define' in Object.prototype ||
Object.defineProperty(Object.prototype, 'define', {
  configurable: true,
  writable: true,
  value: function define(a, b){

    // named function
    if (typeof a === 'function' && a.name.length) {
      b = a, a = a.name;
    }

    // array of named functions
    else if (Array.isArray(a)) {
      var k = a.length;
      while (k--) {
        if (typeof a[k] === 'function' && a[k].name.length) {
          this.define(a[k].name, a[k]);
        }
      }
      return this;
    }

    // dict or an object to absorb properties from
    else if (Object(a) === a) {
      Object.getOwnPropertyNames(a).forEach(function(name){
        this.define(name, Object.getOwnPropertyDescriptor(a, name));
      }, this);
      return this;
    }

    // empty descriptor
    var desc = Object.create(null);

    // "_name" sets non-enumerable
    // "$name" sets non-configurable
    // "_$name" does both
    desc.enumerable = a[0] !== '_' && a !== 'toString';
    desc.configurable = a[desc.enumerable ? 0 : 1] !== '$';

    // chop _ and $
    a = a.substring(2 - desc.enumerable - desc.configurable);

    // accessor descriptor or function as value
    if (typeof b === 'function') {
      // accessor descriptor when "getName" or "setName"
      if (/^[gs]et/.test(a)) {
        desc[a.substring(0, 3)] = b;
        a = a[3].toLowerCase() + a.substring(4);
      }
      // function as value
      else desc.value = b;
    }
    // descriptor or actual value
    else if (Object(b) === b) {
      // accessor descriptor
      if ('get' in b || 'set' in b) {
        if ((b.get && b.get.length === 1) || (b.set && b.set.length === 2)) {
          desc.value = { get: b.get, set: b.set };
        } else {
          desc.get = b.get;
          desc.set = b.set;
        }
      }
      // data descriptor or actual value
      else desc.value = 'value' in b ? b.value : b;
    }

    // primitive
    else desc.value = b;

    // "NAME" sets non-writable for data descriptors
    if ('value' in desc) {
      desc.writable = a !== a.toUpperCase();
    }

    // Support for getter/setter pair with a shared private scope not exposed on the object.
    // They each take an extra parameter which will be the private shared value.

    // The extra parameters cause the property to be interpreterd as a data descriptor with a
    // value of an object with get and set members. This is good because Firefox errors if
    // you try to pass accessors with extra parameters.

    if (desc.value && ((desc.value.get && desc.value.get.length === 1) ||
                       (desc.value.set && desc.value.set.length === 2))) {

      // create a private scope
      desc = (function(desc){
        // shared private
        var _hiddenShared, get = desc.value.get, set = desc.value.set;

        if (get) {
          // wrap getter to maintain the binding and pass in the private
          desc.get = function _getter(){
            // get(privateValue) where `this` is the parent object
            return get.call(this, _hiddenShared);
          };
        }

        if (set) {
          // setter semantics change to requiring the new value to be returned
          // setter gets current (private) value and the setter value
          desc.set = function _setter(newValue){
            // set(privateValue, newValue) where `this` is the parent object
            _hiddenShared = set.call(this, _hiddenShared, newValue);
          };
        }

        // errors abound if either of these are left on the descriptor
        delete desc.writable;
        delete desc.value;

        return desc;
      })(desc);
    }

    // returns `this`
    return Object.defineProperty(this, a, desc);
  }
})
);

(function(d,t,r,g,s){
_gaq=[['_setAccount','UA-21548534-1'],[r+'view'],[r+'LoadTime']];
(g=d.createElement(t)).src='http://www.google-analytics.com/ga.js';g.async=1;
(s=d.getElementsByTagName(t)[0]).parentNode.insertBefore(g,s);
}(document,'script','_trackPage'));
