(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
// Browser Request
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// UMD HEADER START 
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like enviroments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.returnExports = factory();
  }
}(this, function () {
// UMD HEADER END

var XHR = XMLHttpRequest
if (!XHR) throw new Error('missing XMLHttpRequest')
request.log = {
  'trace': noop, 'debug': noop, 'info': noop, 'warn': noop, 'error': noop
}

var DEFAULT_TIMEOUT = 3 * 60 * 1000 // 3 minutes

//
// request
//

function request(options, callback) {
  // The entry-point to the API: prep the options object and pass the real work to run_xhr.
  if(typeof callback !== 'function')
    throw new Error('Bad callback given: ' + callback)

  if(!options)
    throw new Error('No options given')

  var options_onResponse = options.onResponse; // Save this for later.

  if(typeof options === 'string')
    options = {'uri':options};
  else
    options = JSON.parse(JSON.stringify(options)); // Use a duplicate for mutating.

  options.onResponse = options_onResponse // And put it back.

  if (options.verbose) request.log = getLogger();

  if(options.url) {
    options.uri = options.url;
    delete options.url;
  }

  if(!options.uri && options.uri !== "")
    throw new Error("options.uri is a required argument");

  if(typeof options.uri != "string")
    throw new Error("options.uri must be a string");

  var unsupported_options = ['proxy', '_redirectsFollowed', 'maxRedirects', 'followRedirect']
  for (var i = 0; i < unsupported_options.length; i++)
    if(options[ unsupported_options[i] ])
      throw new Error("options." + unsupported_options[i] + " is not supported")

  options.callback = callback
  options.method = options.method || 'GET';
  options.headers = options.headers || {};
  options.body    = options.body || null
  options.timeout = options.timeout || request.DEFAULT_TIMEOUT

  if(options.headers.host)
    throw new Error("Options.headers.host is not supported");

  if(options.json) {
    options.headers.accept = options.headers.accept || 'application/json'
    if(options.method !== 'GET')
      options.headers['content-type'] = 'application/json'

    if(typeof options.json !== 'boolean')
      options.body = JSON.stringify(options.json)
    else if(typeof options.body !== 'string')
      options.body = JSON.stringify(options.body)
  }
  
  //BEGIN QS Hack
  var serialize = function(obj) {
    var str = [];
    for(var p in obj)
      if (obj.hasOwnProperty(p)) {
        str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
      }
    return str.join("&");
  }
  
  if(options.qs){
    var qs = (typeof options.qs == 'string')? options.qs : serialize(options.qs);
    if(options.uri.indexOf('?') !== -1){ //no get params
        options.uri = options.uri+'&'+qs;
    }else{ //existing get params
        options.uri = options.uri+'?'+qs;
    }
  }
  //END QS Hack
  
  //BEGIN FORM Hack
  var multipart = function(obj) {
    //todo: support file type (useful?)
    var result = {};
    result.boundry = '-------------------------------'+Math.floor(Math.random()*1000000000);
    var lines = [];
    for(var p in obj){
        if (obj.hasOwnProperty(p)) {
            lines.push(
                '--'+result.boundry+"\n"+
                'Content-Disposition: form-data; name="'+p+'"'+"\n"+
                "\n"+
                obj[p]+"\n"
            );
        }
    }
    lines.push( '--'+result.boundry+'--' );
    result.body = lines.join('');
    result.length = result.body.length;
    result.type = 'multipart/form-data; boundary='+result.boundry;
    return result;
  }
  
  if(options.form){
    if(typeof options.form == 'string') throw('form name unsupported');
    if(options.method === 'POST'){
        var encoding = (options.encoding || 'application/x-www-form-urlencoded').toLowerCase();
        options.headers['content-type'] = encoding;
        switch(encoding){
            case 'application/x-www-form-urlencoded':
                options.body = serialize(options.form).replace(/%20/g, "+");
                break;
            case 'multipart/form-data':
                var multi = multipart(options.form);
                //options.headers['content-length'] = multi.length;
                options.body = multi.body;
                options.headers['content-type'] = multi.type;
                break;
            default : throw new Error('unsupported encoding:'+encoding);
        }
    }
  }
  //END FORM Hack

  // If onResponse is boolean true, call back immediately when the response is known,
  // not when the full request is complete.
  options.onResponse = options.onResponse || noop
  if(options.onResponse === true) {
    options.onResponse = callback
    options.callback = noop
  }

  // XXX Browsers do not like this.
  //if(options.body)
  //  options.headers['content-length'] = options.body.length;

  // HTTP basic authentication
  if(!options.headers.authorization && options.auth)
    options.headers.authorization = 'Basic ' + b64_enc(options.auth.username + ':' + options.auth.password);

  return run_xhr(options)
}

var req_seq = 0
function run_xhr(options) {
  var xhr = new XHR
    , timed_out = false
    , is_cors = is_crossDomain(options.uri)
    , supports_cors = ('withCredentials' in xhr)

  req_seq += 1
  xhr.seq_id = req_seq
  xhr.id = req_seq + ': ' + options.method + ' ' + options.uri
  xhr._id = xhr.id // I know I will type "_id" from habit all the time.

  if(is_cors && !supports_cors) {
    var cors_err = new Error('Browser does not support cross-origin request: ' + options.uri)
    cors_err.cors = 'unsupported'
    return options.callback(cors_err, xhr)
  }

  xhr.timeoutTimer = setTimeout(too_late, options.timeout)
  function too_late() {
    timed_out = true
    var er = new Error('ETIMEDOUT')
    er.code = 'ETIMEDOUT'
    er.duration = options.timeout

    request.log.error('Timeout', { 'id':xhr._id, 'milliseconds':options.timeout })
    return options.callback(er, xhr)
  }

  // Some states can be skipped over, so remember what is still incomplete.
  var did = {'response':false, 'loading':false, 'end':false}

  xhr.onreadystatechange = on_state_change
  xhr.open(options.method, options.uri, true) // asynchronous
  if(is_cors)
    xhr.withCredentials = !! options.withCredentials
  xhr.send(options.body)
  return xhr

  function on_state_change(event) {
    if(timed_out)
      return request.log.debug('Ignoring timed out state change', {'state':xhr.readyState, 'id':xhr.id})

    request.log.debug('State change', {'state':xhr.readyState, 'id':xhr.id, 'timed_out':timed_out})

    if(xhr.readyState === XHR.OPENED) {
      request.log.debug('Request started', {'id':xhr.id})
      for (var key in options.headers)
        xhr.setRequestHeader(key, options.headers[key])
    }

    else if(xhr.readyState === XHR.HEADERS_RECEIVED)
      on_response()

    else if(xhr.readyState === XHR.LOADING) {
      on_response()
      on_loading()
    }

    else if(xhr.readyState === XHR.DONE) {
      on_response()
      on_loading()
      on_end()
    }
  }

  function on_response() {
    if(did.response)
      return

    did.response = true
    request.log.debug('Got response', {'id':xhr.id, 'status':xhr.status})
    clearTimeout(xhr.timeoutTimer)
    xhr.statusCode = xhr.status // Node request compatibility

    // Detect failed CORS requests.
    if(is_cors && xhr.statusCode == 0) {
      var cors_err = new Error('CORS request rejected: ' + options.uri)
      cors_err.cors = 'rejected'

      // Do not process this request further.
      did.loading = true
      did.end = true

      return options.callback(cors_err, xhr)
    }

    options.onResponse(null, xhr)
  }

  function on_loading() {
    if(did.loading)
      return

    did.loading = true
    request.log.debug('Response body loading', {'id':xhr.id})
    // TODO: Maybe simulate "data" events by watching xhr.responseText
  }

  function on_end() {
    if(did.end)
      return

    did.end = true
    request.log.debug('Request done', {'id':xhr.id})

    xhr.body = xhr.responseText
    if(options.json) {
      try        { xhr.body = JSON.parse(xhr.responseText) }
      catch (er) { return options.callback(er, xhr)        }
    }

    options.callback(null, xhr, xhr.body)
  }

} // request

request.withCredentials = false;
request.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;

//
// defaults
//

request.defaults = function(options, requester) {
  var def = function (method) {
    var d = function (params, callback) {
      if(typeof params === 'string')
        params = {'uri': params};
      else {
        params = JSON.parse(JSON.stringify(params));
      }
      for (var i in options) {
        if (params[i] === undefined) params[i] = options[i]
      }
      return method(params, callback)
    }
    return d
  }
  var de = def(request)
  de.get = def(request.get)
  de.post = def(request.post)
  de.put = def(request.put)
  de.head = def(request.head)
  return de
}

//
// HTTP method shortcuts
//

var shortcuts = [ 'get', 'put', 'post', 'head' ];
shortcuts.forEach(function(shortcut) {
  var method = shortcut.toUpperCase();
  var func   = shortcut.toLowerCase();

  request[func] = function(opts) {
    if(typeof opts === 'string')
      opts = {'method':method, 'uri':opts};
    else {
      opts = JSON.parse(JSON.stringify(opts));
      opts.method = method;
    }

    var args = [opts].concat(Array.prototype.slice.apply(arguments, [1]));
    return request.apply(this, args);
  }
})

//
// CouchDB shortcut
//

request.couch = function(options, callback) {
  if(typeof options === 'string')
    options = {'uri':options}

  // Just use the request API to do JSON.
  options.json = true
  if(options.body)
    options.json = options.body
  delete options.body

  callback = callback || noop

  var xhr = request(options, couch_handler)
  return xhr

  function couch_handler(er, resp, body) {
    if(er)
      return callback(er, resp, body)

    if((resp.statusCode < 200 || resp.statusCode > 299) && body.error) {
      // The body is a Couch JSON object indicating the error.
      er = new Error('CouchDB error: ' + (body.error.reason || body.error.error))
      for (var key in body)
        er[key] = body[key]
      return callback(er, resp, body);
    }

    return callback(er, resp, body);
  }
}

//
// Utility
//

function noop() {}

function getLogger() {
  var logger = {}
    , levels = ['trace', 'debug', 'info', 'warn', 'error']
    , level, i

  for(i = 0; i < levels.length; i++) {
    level = levels[i]

    logger[level] = noop
    if(typeof console !== 'undefined' && console && console[level])
      logger[level] = formatted(console, level)
  }

  return logger
}

function formatted(obj, method) {
  return formatted_logger

  function formatted_logger(str, context) {
    if(typeof context === 'object')
      str += ' ' + JSON.stringify(context)

    return obj[method].call(obj, str)
  }
}

// Return whether a URL is a cross-domain request.
function is_crossDomain(url) {
  var rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/

  // jQuery #8138, IE may throw an exception when accessing
  // a field from window.location if document.domain has been set
  var ajaxLocation
  try { ajaxLocation = location.href }
  catch (e) {
    // Use the href attribute of an A element since IE will modify it given document.location
    ajaxLocation = document.createElement( "a" );
    ajaxLocation.href = "";
    ajaxLocation = ajaxLocation.href;
  }

  var ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || []
    , parts = rurl.exec(url.toLowerCase() )

  var result = !!(
    parts &&
    (  parts[1] != ajaxLocParts[1]
    || parts[2] != ajaxLocParts[2]
    || (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? 80 : 443))
    )
  )

  //console.debug('is_crossDomain('+url+') -> ' + result)
  return result
}

// MIT License from http://phpjs.org/functions/base64_encode:358
function b64_enc (data) {
    // Encodes string using MIME base64 algorithm
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc="", tmp_arr = [];

    if (!data) {
        return data;
    }

    // assume utf8 data
    // data = this.utf8_encode(data+'');

    do { // pack three octets into four hexets
        o1 = data.charCodeAt(i++);
        o2 = data.charCodeAt(i++);
        o3 = data.charCodeAt(i++);

        bits = o1<<16 | o2<<8 | o3;

        h1 = bits>>18 & 0x3f;
        h2 = bits>>12 & 0x3f;
        h3 = bits>>6 & 0x3f;
        h4 = bits & 0x3f;

        // use hexets to index into b64, and append result to encoded string
        tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    } while (i < data.length);

    enc = tmp_arr.join('');

    switch (data.length % 3) {
        case 1:
            enc = enc.slice(0, -2) + '==';
        break;
        case 2:
            enc = enc.slice(0, -1) + '=';
        break;
    }

    return enc;
}
    return request;
//UMD FOOTER START
}));
//UMD FOOTER END

},{}],2:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

function EventEmitter() {
  this._events = this._events || {};
  this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!isNumber(n) || n < 0 || isNaN(n))
    throw TypeError('n must be a positive number');
  this._maxListeners = n;
  return this;
};

EventEmitter.prototype.emit = function(type) {
  var er, handler, len, args, i, listeners;

  if (!this._events)
    this._events = {};

  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events.error ||
        (isObject(this._events.error) && !this._events.error.length)) {
      er = arguments[1];
      if (er instanceof Error) {
        throw er; // Unhandled 'error' event
      }
      throw TypeError('Uncaught, unspecified "error" event.');
    }
  }

  handler = this._events[type];

  if (isUndefined(handler))
    return false;

  if (isFunction(handler)) {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        len = arguments.length;
        args = new Array(len - 1);
        for (i = 1; i < len; i++)
          args[i - 1] = arguments[i];
        handler.apply(this, args);
    }
  } else if (isObject(handler)) {
    len = arguments.length;
    args = new Array(len - 1);
    for (i = 1; i < len; i++)
      args[i - 1] = arguments[i];

    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++)
      listeners[i].apply(this, args);
  }

  return true;
};

EventEmitter.prototype.addListener = function(type, listener) {
  var m;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events)
    this._events = {};

  // To avoid recursion in the case that type === "newListener"! Before
  // adding it to the listeners, first emit "newListener".
  if (this._events.newListener)
    this.emit('newListener', type,
              isFunction(listener.listener) ?
              listener.listener : listener);

  if (!this._events[type])
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  else if (isObject(this._events[type]))
    // If we've already got an array, just append.
    this._events[type].push(listener);
  else
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];

  // Check for listener leak
  if (isObject(this._events[type]) && !this._events[type].warned) {
    var m;
    if (!isUndefined(this._maxListeners)) {
      m = this._maxListeners;
    } else {
      m = EventEmitter.defaultMaxListeners;
    }

    if (m && m > 0 && this._events[type].length > m) {
      this._events[type].warned = true;
      console.error('(node) warning: possible EventEmitter memory ' +
                    'leak detected. %d listeners added. ' +
                    'Use emitter.setMaxListeners() to increase limit.',
                    this._events[type].length);
      if (typeof console.trace === 'function') {
        // not supported in IE 10
        console.trace();
      }
    }
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  var fired = false;

  function g() {
    this.removeListener(type, g);

    if (!fired) {
      fired = true;
      listener.apply(this, arguments);
    }
  }

  g.listener = listener;
  this.on(type, g);

  return this;
};

// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
  var list, position, length, i;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events || !this._events[type])
    return this;

  list = this._events[type];
  length = list.length;
  position = -1;

  if (list === listener ||
      (isFunction(list.listener) && list.listener === listener)) {
    delete this._events[type];
    if (this._events.removeListener)
      this.emit('removeListener', type, listener);

  } else if (isObject(list)) {
    for (i = length; i-- > 0;) {
      if (list[i] === listener ||
          (list[i].listener && list[i].listener === listener)) {
        position = i;
        break;
      }
    }

    if (position < 0)
      return this;

    if (list.length === 1) {
      list.length = 0;
      delete this._events[type];
    } else {
      list.splice(position, 1);
    }

    if (this._events.removeListener)
      this.emit('removeListener', type, listener);
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  var key, listeners;

  if (!this._events)
    return this;

  // not listening for removeListener, no need to emit
  if (!this._events.removeListener) {
    if (arguments.length === 0)
      this._events = {};
    else if (this._events[type])
      delete this._events[type];
    return this;
  }

  // emit removeListener for all listeners on all events
  if (arguments.length === 0) {
    for (key in this._events) {
      if (key === 'removeListener') continue;
      this.removeAllListeners(key);
    }
    this.removeAllListeners('removeListener');
    this._events = {};
    return this;
  }

  listeners = this._events[type];

  if (isFunction(listeners)) {
    this.removeListener(type, listeners);
  } else {
    // LIFO order
    while (listeners.length)
      this.removeListener(type, listeners[listeners.length - 1]);
  }
  delete this._events[type];

  return this;
};

EventEmitter.prototype.listeners = function(type) {
  var ret;
  if (!this._events || !this._events[type])
    ret = [];
  else if (isFunction(this._events[type]))
    ret = [this._events[type]];
  else
    ret = this._events[type].slice();
  return ret;
};

EventEmitter.listenerCount = function(emitter, type) {
  var ret;
  if (!emitter._events || !emitter._events[type])
    ret = 0;
  else if (isFunction(emitter._events[type]))
    ret = 1;
  else
    ret = emitter._events[type].length;
  return ret;
};

function isFunction(arg) {
  return typeof arg === 'function';
}

function isNumber(arg) {
  return typeof arg === 'number';
}

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}

function isUndefined(arg) {
  return arg === void 0;
}

},{}],3:[function(require,module,exports){
// shim for using process in browser

var process = module.exports = {};
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = setTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    clearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        setTimeout(drainQueue, 0);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };

},{}],4:[function(require,module,exports){
(function (process,global){
/*!
 * @overview es6-promise - a tiny implementation of Promises/A+.
 * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
 * @license   Licensed under MIT license
 *            See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
 * @version   3.0.2
 */

(function() {
    "use strict";
    function lib$es6$promise$utils$$objectOrFunction(x) {
      return typeof x === 'function' || (typeof x === 'object' && x !== null);
    }

    function lib$es6$promise$utils$$isFunction(x) {
      return typeof x === 'function';
    }

    function lib$es6$promise$utils$$isMaybeThenable(x) {
      return typeof x === 'object' && x !== null;
    }

    var lib$es6$promise$utils$$_isArray;
    if (!Array.isArray) {
      lib$es6$promise$utils$$_isArray = function (x) {
        return Object.prototype.toString.call(x) === '[object Array]';
      };
    } else {
      lib$es6$promise$utils$$_isArray = Array.isArray;
    }

    var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray;
    var lib$es6$promise$asap$$len = 0;
    var lib$es6$promise$asap$$toString = {}.toString;
    var lib$es6$promise$asap$$vertxNext;
    var lib$es6$promise$asap$$customSchedulerFn;

    var lib$es6$promise$asap$$asap = function asap(callback, arg) {
      lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback;
      lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg;
      lib$es6$promise$asap$$len += 2;
      if (lib$es6$promise$asap$$len === 2) {
        // If len is 2, that means that we need to schedule an async flush.
        // If additional callbacks are queued before the queue is flushed, they
        // will be processed by this flush that we are scheduling.
        if (lib$es6$promise$asap$$customSchedulerFn) {
          lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush);
        } else {
          lib$es6$promise$asap$$scheduleFlush();
        }
      }
    }

    function lib$es6$promise$asap$$setScheduler(scheduleFn) {
      lib$es6$promise$asap$$customSchedulerFn = scheduleFn;
    }

    function lib$es6$promise$asap$$setAsap(asapFn) {
      lib$es6$promise$asap$$asap = asapFn;
    }

    var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined;
    var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {};
    var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver;
    var lib$es6$promise$asap$$isNode = typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';

    // test for web worker but not in IE10
    var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
      typeof importScripts !== 'undefined' &&
      typeof MessageChannel !== 'undefined';

    // node
    function lib$es6$promise$asap$$useNextTick() {
      // node version 0.10.x displays a deprecation warning when nextTick is used recursively
      // see https://github.com/cujojs/when/issues/410 for details
      return function() {
        process.nextTick(lib$es6$promise$asap$$flush);
      };
    }

    // vertx
    function lib$es6$promise$asap$$useVertxTimer() {
      return function() {
        lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush);
      };
    }

    function lib$es6$promise$asap$$useMutationObserver() {
      var iterations = 0;
      var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush);
      var node = document.createTextNode('');
      observer.observe(node, { characterData: true });

      return function() {
        node.data = (iterations = ++iterations % 2);
      };
    }

    // web worker
    function lib$es6$promise$asap$$useMessageChannel() {
      var channel = new MessageChannel();
      channel.port1.onmessage = lib$es6$promise$asap$$flush;
      return function () {
        channel.port2.postMessage(0);
      };
    }

    function lib$es6$promise$asap$$useSetTimeout() {
      return function() {
        setTimeout(lib$es6$promise$asap$$flush, 1);
      };
    }

    var lib$es6$promise$asap$$queue = new Array(1000);
    function lib$es6$promise$asap$$flush() {
      for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) {
        var callback = lib$es6$promise$asap$$queue[i];
        var arg = lib$es6$promise$asap$$queue[i+1];

        callback(arg);

        lib$es6$promise$asap$$queue[i] = undefined;
        lib$es6$promise$asap$$queue[i+1] = undefined;
      }

      lib$es6$promise$asap$$len = 0;
    }

    function lib$es6$promise$asap$$attemptVertx() {
      try {
        var r = require;
        var vertx = r('vertx');
        lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext;
        return lib$es6$promise$asap$$useVertxTimer();
      } catch(e) {
        return lib$es6$promise$asap$$useSetTimeout();
      }
    }

    var lib$es6$promise$asap$$scheduleFlush;
    // Decide what async method to use to triggering processing of queued callbacks:
    if (lib$es6$promise$asap$$isNode) {
      lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick();
    } else if (lib$es6$promise$asap$$BrowserMutationObserver) {
      lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver();
    } else if (lib$es6$promise$asap$$isWorker) {
      lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel();
    } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') {
      lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertx();
    } else {
      lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout();
    }

    function lib$es6$promise$$internal$$noop() {}

    var lib$es6$promise$$internal$$PENDING   = void 0;
    var lib$es6$promise$$internal$$FULFILLED = 1;
    var lib$es6$promise$$internal$$REJECTED  = 2;

    var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject();

    function lib$es6$promise$$internal$$selfFulfillment() {
      return new TypeError("You cannot resolve a promise with itself");
    }

    function lib$es6$promise$$internal$$cannotReturnOwn() {
      return new TypeError('A promises callback cannot return that same promise.');
    }

    function lib$es6$promise$$internal$$getThen(promise) {
      try {
        return promise.then;
      } catch(error) {
        lib$es6$promise$$internal$$GET_THEN_ERROR.error = error;
        return lib$es6$promise$$internal$$GET_THEN_ERROR;
      }
    }

    function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
      try {
        then.call(value, fulfillmentHandler, rejectionHandler);
      } catch(e) {
        return e;
      }
    }

    function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) {
       lib$es6$promise$asap$$asap(function(promise) {
        var sealed = false;
        var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) {
          if (sealed) { return; }
          sealed = true;
          if (thenable !== value) {
            lib$es6$promise$$internal$$resolve(promise, value);
          } else {
            lib$es6$promise$$internal$$fulfill(promise, value);
          }
        }, function(reason) {
          if (sealed) { return; }
          sealed = true;

          lib$es6$promise$$internal$$reject(promise, reason);
        }, 'Settle: ' + (promise._label || ' unknown promise'));

        if (!sealed && error) {
          sealed = true;
          lib$es6$promise$$internal$$reject(promise, error);
        }
      }, promise);
    }

    function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) {
      if (thenable._state === lib$es6$promise$$internal$$FULFILLED) {
        lib$es6$promise$$internal$$fulfill(promise, thenable._result);
      } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) {
        lib$es6$promise$$internal$$reject(promise, thenable._result);
      } else {
        lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) {
          lib$es6$promise$$internal$$resolve(promise, value);
        }, function(reason) {
          lib$es6$promise$$internal$$reject(promise, reason);
        });
      }
    }

    function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable) {
      if (maybeThenable.constructor === promise.constructor) {
        lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable);
      } else {
        var then = lib$es6$promise$$internal$$getThen(maybeThenable);

        if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) {
          lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error);
        } else if (then === undefined) {
          lib$es6$promise$$internal$$fulfill(promise, maybeThenable);
        } else if (lib$es6$promise$utils$$isFunction(then)) {
          lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then);
        } else {
          lib$es6$promise$$internal$$fulfill(promise, maybeThenable);
        }
      }
    }

    function lib$es6$promise$$internal$$resolve(promise, value) {
      if (promise === value) {
        lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFulfillment());
      } else if (lib$es6$promise$utils$$objectOrFunction(value)) {
        lib$es6$promise$$internal$$handleMaybeThenable(promise, value);
      } else {
        lib$es6$promise$$internal$$fulfill(promise, value);
      }
    }

    function lib$es6$promise$$internal$$publishRejection(promise) {
      if (promise._onerror) {
        promise._onerror(promise._result);
      }

      lib$es6$promise$$internal$$publish(promise);
    }

    function lib$es6$promise$$internal$$fulfill(promise, value) {
      if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; }

      promise._result = value;
      promise._state = lib$es6$promise$$internal$$FULFILLED;

      if (promise._subscribers.length !== 0) {
        lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise);
      }
    }

    function lib$es6$promise$$internal$$reject(promise, reason) {
      if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; }
      promise._state = lib$es6$promise$$internal$$REJECTED;
      promise._result = reason;

      lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise);
    }

    function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
      var subscribers = parent._subscribers;
      var length = subscribers.length;

      parent._onerror = null;

      subscribers[length] = child;
      subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment;
      subscribers[length + lib$es6$promise$$internal$$REJECTED]  = onRejection;

      if (length === 0 && parent._state) {
        lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent);
      }
    }

    function lib$es6$promise$$internal$$publish(promise) {
      var subscribers = promise._subscribers;
      var settled = promise._state;

      if (subscribers.length === 0) { return; }

      var child, callback, detail = promise._result;

      for (var i = 0; i < subscribers.length; i += 3) {
        child = subscribers[i];
        callback = subscribers[i + settled];

        if (child) {
          lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail);
        } else {
          callback(detail);
        }
      }

      promise._subscribers.length = 0;
    }

    function lib$es6$promise$$internal$$ErrorObject() {
      this.error = null;
    }

    var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject();

    function lib$es6$promise$$internal$$tryCatch(callback, detail) {
      try {
        return callback(detail);
      } catch(e) {
        lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e;
        return lib$es6$promise$$internal$$TRY_CATCH_ERROR;
      }
    }

    function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) {
      var hasCallback = lib$es6$promise$utils$$isFunction(callback),
          value, error, succeeded, failed;

      if (hasCallback) {
        value = lib$es6$promise$$internal$$tryCatch(callback, detail);

        if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) {
          failed = true;
          error = value.error;
          value = null;
        } else {
          succeeded = true;
        }

        if (promise === value) {
          lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn());
          return;
        }

      } else {
        value = detail;
        succeeded = true;
      }

      if (promise._state !== lib$es6$promise$$internal$$PENDING) {
        // noop
      } else if (hasCallback && succeeded) {
        lib$es6$promise$$internal$$resolve(promise, value);
      } else if (failed) {
        lib$es6$promise$$internal$$reject(promise, error);
      } else if (settled === lib$es6$promise$$internal$$FULFILLED) {
        lib$es6$promise$$internal$$fulfill(promise, value);
      } else if (settled === lib$es6$promise$$internal$$REJECTED) {
        lib$es6$promise$$internal$$reject(promise, value);
      }
    }

    function lib$es6$promise$$internal$$initializePromise(promise, resolver) {
      try {
        resolver(function resolvePromise(value){
          lib$es6$promise$$internal$$resolve(promise, value);
        }, function rejectPromise(reason) {
          lib$es6$promise$$internal$$reject(promise, reason);
        });
      } catch(e) {
        lib$es6$promise$$internal$$reject(promise, e);
      }
    }

    function lib$es6$promise$enumerator$$Enumerator(Constructor, input) {
      var enumerator = this;

      enumerator._instanceConstructor = Constructor;
      enumerator.promise = new Constructor(lib$es6$promise$$internal$$noop);

      if (enumerator._validateInput(input)) {
        enumerator._input     = input;
        enumerator.length     = input.length;
        enumerator._remaining = input.length;

        enumerator._init();

        if (enumerator.length === 0) {
          lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result);
        } else {
          enumerator.length = enumerator.length || 0;
          enumerator._enumerate();
          if (enumerator._remaining === 0) {
            lib$es6$promise$$internal$$fulfill(enumerator.promise, enumerator._result);
          }
        }
      } else {
        lib$es6$promise$$internal$$reject(enumerator.promise, enumerator._validationError());
      }
    }

    lib$es6$promise$enumerator$$Enumerator.prototype._validateInput = function(input) {
      return lib$es6$promise$utils$$isArray(input);
    };

    lib$es6$promise$enumerator$$Enumerator.prototype._validationError = function() {
      return new Error('Array Methods must be provided an Array');
    };

    lib$es6$promise$enumerator$$Enumerator.prototype._init = function() {
      this._result = new Array(this.length);
    };

    var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator;

    lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() {
      var enumerator = this;

      var length  = enumerator.length;
      var promise = enumerator.promise;
      var input   = enumerator._input;

      for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) {
        enumerator._eachEntry(input[i], i);
      }
    };

    lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
      var enumerator = this;
      var c = enumerator._instanceConstructor;

      if (lib$es6$promise$utils$$isMaybeThenable(entry)) {
        if (entry.constructor === c && entry._state !== lib$es6$promise$$internal$$PENDING) {
          entry._onerror = null;
          enumerator._settledAt(entry._state, i, entry._result);
        } else {
          enumerator._willSettleAt(c.resolve(entry), i);
        }
      } else {
        enumerator._remaining--;
        enumerator._result[i] = entry;
      }
    };

    lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
      var enumerator = this;
      var promise = enumerator.promise;

      if (promise._state === lib$es6$promise$$internal$$PENDING) {
        enumerator._remaining--;

        if (state === lib$es6$promise$$internal$$REJECTED) {
          lib$es6$promise$$internal$$reject(promise, value);
        } else {
          enumerator._result[i] = value;
        }
      }

      if (enumerator._remaining === 0) {
        lib$es6$promise$$internal$$fulfill(promise, enumerator._result);
      }
    };

    lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
      var enumerator = this;

      lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) {
        enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value);
      }, function(reason) {
        enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason);
      });
    };
    function lib$es6$promise$promise$all$$all(entries) {
      return new lib$es6$promise$enumerator$$default(this, entries).promise;
    }
    var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all;
    function lib$es6$promise$promise$race$$race(entries) {
      /*jshint validthis:true */
      var Constructor = this;

      var promise = new Constructor(lib$es6$promise$$internal$$noop);

      if (!lib$es6$promise$utils$$isArray(entries)) {
        lib$es6$promise$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
        return promise;
      }

      var length = entries.length;

      function onFulfillment(value) {
        lib$es6$promise$$internal$$resolve(promise, value);
      }

      function onRejection(reason) {
        lib$es6$promise$$internal$$reject(promise, reason);
      }

      for (var i = 0; promise._state === lib$es6$promise$$internal$$PENDING && i < length; i++) {
        lib$es6$promise$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
      }

      return promise;
    }
    var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race;
    function lib$es6$promise$promise$resolve$$resolve(object) {
      /*jshint validthis:true */
      var Constructor = this;

      if (object && typeof object === 'object' && object.constructor === Constructor) {
        return object;
      }

      var promise = new Constructor(lib$es6$promise$$internal$$noop);
      lib$es6$promise$$internal$$resolve(promise, object);
      return promise;
    }
    var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve;
    function lib$es6$promise$promise$reject$$reject(reason) {
      /*jshint validthis:true */
      var Constructor = this;
      var promise = new Constructor(lib$es6$promise$$internal$$noop);
      lib$es6$promise$$internal$$reject(promise, reason);
      return promise;
    }
    var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject;

    var lib$es6$promise$promise$$counter = 0;

    function lib$es6$promise$promise$$needsResolver() {
      throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
    }

    function lib$es6$promise$promise$$needsNew() {
      throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
    }

    var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise;
    /**
      Promise objects represent the eventual result of an asynchronous operation. The
      primary way of interacting with a promise is through its `then` method, which
      registers callbacks to receive either a promise's eventual value or the reason
      why the promise cannot be fulfilled.

      Terminology
      -----------

      - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
      - `thenable` is an object or function that defines a `then` method.
      - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
      - `exception` is a value that is thrown using the throw statement.
      - `reason` is a value that indicates why a promise was rejected.
      - `settled` the final resting state of a promise, fulfilled or rejected.

      A promise can be in one of three states: pending, fulfilled, or rejected.

      Promises that are fulfilled have a fulfillment value and are in the fulfilled
      state.  Promises that are rejected have a rejection reason and are in the
      rejected state.  A fulfillment value is never a thenable.

      Promises can also be said to *resolve* a value.  If this value is also a
      promise, then the original promise's settled state will match the value's
      settled state.  So a promise that *resolves* a promise that rejects will
      itself reject, and a promise that *resolves* a promise that fulfills will
      itself fulfill.


      Basic Usage:
      ------------

      ```js
      var promise = new Promise(function(resolve, reject) {
        // on success
        resolve(value);

        // on failure
        reject(reason);
      });

      promise.then(function(value) {
        // on fulfillment
      }, function(reason) {
        // on rejection
      });
      ```

      Advanced Usage:
      ---------------

      Promises shine when abstracting away asynchronous interactions such as
      `XMLHttpRequest`s.

      ```js
      function getJSON(url) {
        return new Promise(function(resolve, reject){
          var xhr = new XMLHttpRequest();

          xhr.open('GET', url);
          xhr.onreadystatechange = handler;
          xhr.responseType = 'json';
          xhr.setRequestHeader('Accept', 'application/json');
          xhr.send();

          function handler() {
            if (this.readyState === this.DONE) {
              if (this.status === 200) {
                resolve(this.response);
              } else {
                reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
              }
            }
          };
        });
      }

      getJSON('/posts.json').then(function(json) {
        // on fulfillment
      }, function(reason) {
        // on rejection
      });
      ```

      Unlike callbacks, promises are great composable primitives.

      ```js
      Promise.all([
        getJSON('/posts'),
        getJSON('/comments')
      ]).then(function(values){
        values[0] // => postsJSON
        values[1] // => commentsJSON

        return values;
      });
      ```

      @class Promise
      @param {function} resolver
      Useful for tooling.
      @constructor
    */
    function lib$es6$promise$promise$$Promise(resolver) {
      this._id = lib$es6$promise$promise$$counter++;
      this._state = undefined;
      this._result = undefined;
      this._subscribers = [];

      if (lib$es6$promise$$internal$$noop !== resolver) {
        if (!lib$es6$promise$utils$$isFunction(resolver)) {
          lib$es6$promise$promise$$needsResolver();
        }

        if (!(this instanceof lib$es6$promise$promise$$Promise)) {
          lib$es6$promise$promise$$needsNew();
        }

        lib$es6$promise$$internal$$initializePromise(this, resolver);
      }
    }

    lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default;
    lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default;
    lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default;
    lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default;
    lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler;
    lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap;
    lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap;

    lib$es6$promise$promise$$Promise.prototype = {
      constructor: lib$es6$promise$promise$$Promise,

    /**
      The primary way of interacting with a promise is through its `then` method,
      which registers callbacks to receive either a promise's eventual value or the
      reason why the promise cannot be fulfilled.

      ```js
      findUser().then(function(user){
        // user is available
      }, function(reason){
        // user is unavailable, and you are given the reason why
      });
      ```

      Chaining
      --------

      The return value of `then` is itself a promise.  This second, 'downstream'
      promise is resolved with the return value of the first promise's fulfillment
      or rejection handler, or rejected if the handler throws an exception.

      ```js
      findUser().then(function (user) {
        return user.name;
      }, function (reason) {
        return 'default name';
      }).then(function (userName) {
        // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
        // will be `'default name'`
      });

      findUser().then(function (user) {
        throw new Error('Found user, but still unhappy');
      }, function (reason) {
        throw new Error('`findUser` rejected and we're unhappy');
      }).then(function (value) {
        // never reached
      }, function (reason) {
        // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
        // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
      });
      ```
      If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.

      ```js
      findUser().then(function (user) {
        throw new PedagogicalException('Upstream error');
      }).then(function (value) {
        // never reached
      }).then(function (value) {
        // never reached
      }, function (reason) {
        // The `PedgagocialException` is propagated all the way down to here
      });
      ```

      Assimilation
      ------------

      Sometimes the value you want to propagate to a downstream promise can only be
      retrieved asynchronously. This can be achieved by returning a promise in the
      fulfillment or rejection handler. The downstream promise will then be pending
      until the returned promise is settled. This is called *assimilation*.

      ```js
      findUser().then(function (user) {
        return findCommentsByAuthor(user);
      }).then(function (comments) {
        // The user's comments are now available
      });
      ```

      If the assimliated promise rejects, then the downstream promise will also reject.

      ```js
      findUser().then(function (user) {
        return findCommentsByAuthor(user);
      }).then(function (comments) {
        // If `findCommentsByAuthor` fulfills, we'll have the value here
      }, function (reason) {
        // If `findCommentsByAuthor` rejects, we'll have the reason here
      });
      ```

      Simple Example
      --------------

      Synchronous Example

      ```javascript
      var result;

      try {
        result = findResult();
        // success
      } catch(reason) {
        // failure
      }
      ```

      Errback Example

      ```js
      findResult(function(result, err){
        if (err) {
          // failure
        } else {
          // success
        }
      });
      ```

      Promise Example;

      ```javascript
      findResult().then(function(result){
        // success
      }, function(reason){
        // failure
      });
      ```

      Advanced Example
      --------------

      Synchronous Example

      ```javascript
      var author, books;

      try {
        author = findAuthor();
        books  = findBooksByAuthor(author);
        // success
      } catch(reason) {
        // failure
      }
      ```

      Errback Example

      ```js

      function foundBooks(books) {

      }

      function failure(reason) {

      }

      findAuthor(function(author, err){
        if (err) {
          failure(err);
          // failure
        } else {
          try {
            findBoooksByAuthor(author, function(books, err) {
              if (err) {
                failure(err);
              } else {
                try {
                  foundBooks(books);
                } catch(reason) {
                  failure(reason);
                }
              }
            });
          } catch(error) {
            failure(err);
          }
          // success
        }
      });
      ```

      Promise Example;

      ```javascript
      findAuthor().
        then(findBooksByAuthor).
        then(function(books){
          // found books
      }).catch(function(reason){
        // something went wrong
      });
      ```

      @method then
      @param {Function} onFulfilled
      @param {Function} onRejected
      Useful for tooling.
      @return {Promise}
    */
      then: function(onFulfillment, onRejection) {
        var parent = this;
        var state = parent._state;

        if (state === lib$es6$promise$$internal$$FULFILLED && !onFulfillment || state === lib$es6$promise$$internal$$REJECTED && !onRejection) {
          return this;
        }

        var child = new this.constructor(lib$es6$promise$$internal$$noop);
        var result = parent._result;

        if (state) {
          var callback = arguments[state - 1];
          lib$es6$promise$asap$$asap(function(){
            lib$es6$promise$$internal$$invokeCallback(state, child, callback, result);
          });
        } else {
          lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection);
        }

        return child;
      },

    /**
      `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
      as the catch block of a try/catch statement.

      ```js
      function findAuthor(){
        throw new Error('couldn't find that author');
      }

      // synchronous
      try {
        findAuthor();
      } catch(reason) {
        // something went wrong
      }

      // async with promises
      findAuthor().catch(function(reason){
        // something went wrong
      });
      ```

      @method catch
      @param {Function} onRejection
      Useful for tooling.
      @return {Promise}
    */
      'catch': function(onRejection) {
        return this.then(null, onRejection);
      }
    };
    function lib$es6$promise$polyfill$$polyfill() {
      var local;

      if (typeof global !== 'undefined') {
          local = global;
      } else if (typeof self !== 'undefined') {
          local = self;
      } else {
          try {
              local = Function('return this')();
          } catch (e) {
              throw new Error('polyfill failed because global object is unavailable in this environment');
          }
      }

      var P = local.Promise;

      if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) {
        return;
      }

      local.Promise = lib$es6$promise$promise$$default;
    }
    var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill;

    var lib$es6$promise$umd$$ES6Promise = {
      'Promise': lib$es6$promise$promise$$default,
      'polyfill': lib$es6$promise$polyfill$$default
    };

    /* global define:true module:true window: true */
    if (typeof define === 'function' && define['amd']) {
      define(function() { return lib$es6$promise$umd$$ES6Promise; });
    } else if (typeof module !== 'undefined' && module['exports']) {
      module['exports'] = lib$es6$promise$umd$$ES6Promise;
    } else if (typeof this !== 'undefined') {
      this['ES6Promise'] = lib$es6$promise$umd$$ES6Promise;
    }

    lib$es6$promise$polyfill$$default();
}).call(this);


}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"_process":3}],5:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _coreRequest = require("../core/request");

var _corePromise = require("../core/promise");

var _coreSettings = require("../core/settings");

var _coreCore = require("../core/core");

var _coreLogger = require("../core/logger");

var _storage = require("./storage");

var _coreUser = require("../core/user");

var settings = new _coreSettings.Settings();

var ANALYTICS_KEY = null;
var DEFER_REGISTER = "DEFER_REGISTER";
var options = {};
var globalProperties = {};
var globalPropertiesFns = [];

var Analytics = (function () {
  function Analytics(config) {
    _classCallCheck(this, Analytics);

    this._dispatcher = null;
    this._dispatchIntervalTime = 30;
    this._useEventCaching = true;
    this._serviceHost = settings.getURL('analytics');

    this.logger = new _coreLogger.Logger({
      'prefix': 'Ionic Analytics:'
    });

    this.logger._silence = true;

    this.storage = _coreCore.IonicPlatform.getStorage();
    this.cache = new _storage.BucketStorage('ionic_analytics');
    this._addGlobalPropertyDefaults();
    if (config !== DEFER_REGISTER) {
      this.register(config);
    }
  }

  _createClass(Analytics, [{
    key: "_addGlobalPropertyDefaults",
    value: function _addGlobalPropertyDefaults() {
      var self = this;
      self.setGlobalProperties(function (eventCollection, eventData) {
        eventData._user = JSON.parse(JSON.stringify(_coreUser.User.current()));
        eventData._app = {
          "app_id": settings.get('app_id'), // eslint-disable-line
          "analytics_version": _coreCore.IonicPlatform.Version
        };
      });
    }
  }, {
    key: "_enqueueEvent",
    value: function _enqueueEvent(collectionName, eventData) {
      var self = this;
      if (options.dryRun) {
        self.logger.info('event recieved but not sent (dryRun active):');
        self.logger.info(collectionName);
        self.logger.info(eventData);
        return;
      }

      self.logger.info('enqueuing event to send later:');
      self.logger.info(collectionName);
      self.logger.info(eventData);

      // Add timestamp property to the data
      if (!eventData.keen) {
        eventData.keen = {};
      }
      eventData.keen.timestamp = new Date().toISOString();

      // Add the data to the queue
      var eventQueue = self.cache.get('event_queue') || {};
      if (!eventQueue[collectionName]) {
        eventQueue[collectionName] = [];
      }
      eventQueue[collectionName].push(eventData);

      // Write the queue to disk
      self.cache.set('event_queue', eventQueue);
    }
  }, {
    key: "_requestAnalyticsKey",
    value: function _requestAnalyticsKey() {
      var requestOptions = {
        "method": 'GET',
        "json": true,
        "uri": settings.getURL('api') + '/api/v1/app/' + settings.get('app_id') + '/keys/write',
        'headers': {
          'Authorization': "basic " + btoa(settings.get('app_id') + ':' + settings.get('api_key'))
        }
      };

      return new _coreRequest.APIRequest(requestOptions);
    }
  }, {
    key: "_postEvent",
    value: function _postEvent(name, data) {
      var self = this;
      var payload = {
        "name": [data]
      };

      if (!ANALYTICS_KEY) {
        self.logger.error('Cannot send events to the analytics server without an Analytics key.');
      }

      var requestOptions = {
        "method": 'POST',
        "url": self._serviceHost + '/api/v1/events/' + settings.get('app_id'),
        "body": payload,
        "json": true,
        "headers": {
          "Authorization": ANALYTICS_KEY
        }
      };

      return new _coreRequest.APIRequest(requestOptions);
    }
  }, {
    key: "_postEvents",
    value: function _postEvents(events) {
      var self = this;
      if (!ANALYTICS_KEY) {
        self.logger.info('Cannot send events to the analytics server without an Analytics key.');
      }

      var requestOptions = {
        "method": 'POST',
        "url": self._serviceHost + '/api/v1/events/' + settings.get('app_id'),
        "body": events,
        "json": true,
        "headers": {
          "Authorization": ANALYTICS_KEY
        }
      };

      return new _coreRequest.APIRequest(requestOptions);
    }
  }, {
    key: "_dispatchQueue",
    value: function _dispatchQueue() {
      var self = this;
      var eventQueue = this.cache.get('event_queue') || {};

      if (Object.keys(eventQueue).length === 0) {
        return;
      }

      if (!_coreCore.IonicPlatform.deviceConnectedToNetwork()) {
        return;
      }

      self.storage.lockedAsyncCall(self.cache.scopedKey('event_dispatch'), function () {
        return self._postEvents(eventQueue);
      }).then(function () {
        self.cache.set('event_queue', {});
        self.logger.info('sent events');
        self.logger.info(eventQueue);
      }, function (err) {
        self._handleDispatchError(err, this, eventQueue);
      });
    }
  }, {
    key: "_getRequestStatusCode",
    value: function _getRequestStatusCode(request) {
      var responseCode = false;
      if (request && request.requestInfo._lastResponse && request.requestInfo._lastResponse.statusCode) {
        responseCode = request.requestInfo._lastResponse.statusCode;
      }
      return responseCode;
    }
  }, {
    key: "_handleDispatchError",
    value: function _handleDispatchError(error, request, eventQueue) {
      var self = this;
      var responseCode = this._getRequestStatusCode(request);
      if (error === 'last_call_interrupted') {
        self.cache.set('event_queue', {});
      } else {
        // If we didn't connect to the server at all -> keep events
        if (!responseCode) {
          self.logger.error('Error sending analytics data: Failed to connect to analytics server.');
        } else {
          self.cache.set('event_queue', {});
          self.logger.error('Error sending analytics data: Server responded with error');
          self.logger.error(eventQueue);
        }
      }
    }
  }, {
    key: "_handleRegisterError",
    value: function _handleRegisterError(error, request) {
      var responseCode = this._getRequestStatusCode(request);
      var docs = ' See http://docs.ionic.io/v1.0/docs/io-quick-start';

      switch (responseCode) {
        case 401:
          self.logger.error('The api key and app id you provided did not register on the server. ' + docs);
          break;

        case 404:
          self.logger.error('The app id you provided ("' + settings.get('app_id') + '") was not found.' + docs);
          break;

        default:
          self.logger.error('Unable to request analytics key.');
          self.logger.error(error);
          break;
      }
    }

    /**
     * Registers an analytics key
     *
     * @param {object} opts Registration options
     * @return {Promise} The register promise
     */
  }, {
    key: "register",
    value: function register(opts) {

      var self = this;
      var deferred = new _corePromise.DeferredPromise();

      if (!this.hasValidSettings) {
        deferred.reject(false);
        return deferred.promise;
      }

      options = opts || {};
      if (options.silent) {
        this.logger._silence = true;
      } else {
        this.logger._silence = false;
      }

      if (options.dryRun) {
        this.logger.info('dryRun mode is active. Analytics will not send any events.');
      }

      this._requestAnalyticsKey().then(function (result) {
        ANALYTICS_KEY = result.payload.write_key;
        self.logger.info('successfully registered analytics key');
        self.dispatchInterval = self.dispatchInterval;
        deferred.resolve(true);
      }, function (error) {
        self._handleRegisterError(error, this);
        deferred.reject(false);
      });

      return deferred.promise;
    }
  }, {
    key: "setGlobalProperties",
    value: function setGlobalProperties(prop) {
      var self = this;
      var propType = typeof prop;
      switch (propType) {
        case 'object':
          for (var key in prop) {
            if (!prop.hasOwnProperty(key)) {
              continue;
            }
            globalProperties[key] = prop[key];
          }
          break;

        case 'function':
          globalPropertiesFns.push(prop);
          break;

        default:
          self.logger.error('setGlobalProperties parameter must be an object or function.');
          break;
      }
    }
  }, {
    key: "track",
    value: function track(eventCollection, eventData) {
      var self = this;
      if (!this.hasValidSettings) {
        return false;
      }
      if (!eventData) {
        eventData = {};
      }

      for (var key in globalProperties) {
        if (!globalProperties.hasOwnProperty(key)) {
          continue;
        }

        if (eventData[key] === void 0) {
          eventData[key] = globalProperties[key];
        }
      }

      for (var i = 0; i < globalPropertiesFns.length; i++) {
        var fn = globalPropertiesFns[i];
        fn.call(null, eventCollection, eventData);
      }

      if (this._useEventCaching) {
        self._enqueueEvent(eventCollection, eventData);
      } else {
        if (options.dryRun) {
          self.logger.info('dryRun active, will not send event');
          self.logger.info(eventCollection);
          self.logger.info(eventData);
        } else {
          self._postEvent(eventCollection, eventData);
        }
      }
    }
  }, {
    key: "unsetGlobalProperty",
    value: function unsetGlobalProperty(prop) {
      var self = this;
      var propType = typeof prop;
      switch (propType) {
        case 'string':
          delete globalProperties[prop];
          break;

        case 'function':
          var i = globalPropertiesFns.indexOf(prop);
          if (i === -1) {
            self.logger.error('The function passed to unsetGlobalProperty was not a global property.');
          }
          globalPropertiesFns.splice(i, 1);
          break;

        default:
          self.logger.error('unsetGlobalProperty parameter must be a string or function.');
          break;
      }
    }
  }, {
    key: "hasValidSettings",
    get: function get() {
      if (!settings.get('app_id') || !settings.get('api_key')) {
        var msg = 'A valid app_id and api_key are required before you can utilize ' + 'analytics properly. See http://docs.ionic.io/v1.0/docs/io-quick-start';
        this.logger.info(msg);
        return false;
      }
      return true;
    }
  }, {
    key: "dispatchInterval",
    set: function set(value) {
      var self = this;
      // Set how often we should send batched events, in seconds.
      // Set this to 0 to disable event caching
      this._dispatchIntervalTime = value;

      // Clear the existing interval
      if (this._dispatcher) {
        window.clearInterval(this.dispatcher);
      }

      if (value > 0) {
        this._dispatcher = window.setInterval(function () {
          self._dispatchQueue();
        }, value * 1000);
        this._useEventCaching = true;
      } else {
        this._useEventCaching = false;
      }
    },
    get: function get() {
      return this._dispatchIntervalTime;
    }
  }]);

  return Analytics;
})();

exports.Analytics = Analytics;

},{"../core/core":12,"../core/logger":16,"../core/promise":17,"../core/request":18,"../core/settings":19,"../core/user":21,"./storage":9}],6:[function(require,module,exports){
// Add Angular integrations if Angular is available
'use strict';

if (typeof angular === 'object' && angular.module) {

  /**
   * Generic directive to create auto event handling analytics directives like:
   *
   * <button ion-track-click="eventName">Click Track</button>
   * <button ion-track-hold="eventName">Hold Track</button>
   * <button ion-track-tap="eventName">Tap Track</button>
   * <button ion-track-doubletap="eventName">Double Tap Track</button>
   *
   * @param {string} domEventName The DOM event name
   * @return {array} Angular Directive declaration
   */

  var ionTrackDirective = function ionTrackDirective(domEventName) {
    // eslint-disable-line
    return ['$ionicAnalytics', '$ionicGesture', function ($ionicAnalytics, $ionicGesture) {

      var gestureDriven = ['drag', 'dragstart', 'dragend', 'dragleft', 'dragright', 'dragup', 'dragdown', 'swipe', 'swipeleft', 'swiperight', 'swipeup', 'swipedown', 'tap', 'doubletap', 'hold', 'transform', 'pinch', 'pinchin', 'pinchout', 'rotate'];
      // Check if we need to use the gesture subsystem or the DOM system
      var isGestureDriven = false;
      for (var i = 0; i < gestureDriven.length; i++) {
        if (gestureDriven[i] === domEventName.toLowerCase()) {
          isGestureDriven = true;
        }
      }
      return {
        "restrict": 'A',
        "link": function link($scope, $element, $attr) {
          var capitalized = domEventName[0].toUpperCase() + domEventName.slice(1);
          // Grab event name we will send
          var eventName = $attr['ionTrack' + capitalized];

          if (isGestureDriven) {
            var gesture = $ionicGesture.on(domEventName, handler, $element);
            $scope.$on('$destroy', function () {
              $ionicGesture.off(gesture, domEventName, handler);
            });
          } else {
            $element.on(domEventName, handler);
            $scope.$on('$destroy', function () {
              $element.off(domEventName, handler);
            });
          }

          function handler(e) {
            var eventData = $scope.$eval($attr.ionTrackData) || {};
            if (eventName) {
              $ionicAnalytics.track(eventName, eventData);
            } else {
              $ionicAnalytics.trackClick(e.pageX, e.pageY, e.target, {
                "data": eventData
              });
            }
          }
        }
      };
    }];
  };

  var IonicAngularAnalytics = null;

  angular.module('ionic.service.analytics', ['ionic']).value('IONIC_ANALYTICS_VERSION', Ionic.Analytics.version).factory('$ionicAnalytics', [function () {
    if (!IonicAngularAnalytics) {
      IonicAngularAnalytics = new Ionic.Analytics("DEFER_REGISTER");
    }
    return IonicAngularAnalytics;
  }]).factory('domSerializer', [function () {
    return new Ionic.AnalyticSerializers.DOMSerializer();
  }]).run(['$ionicAnalytics', '$state', function ($ionicAnalytics, $state) {
    $ionicAnalytics.setGlobalProperties(function (eventCollection, eventData) {
      if (!eventData._ui) {
        eventData._ui = {};
      }
      eventData._ui.active_state = $state.current.name; // eslint-disable-line
    });
  }]);

  angular.module('ionic.service.analytics').provider('$ionicAutoTrack', [function () {

    var trackersDisabled = {},
        allTrackersDisabled = false;

    this.disableTracking = function (tracker) {
      if (tracker) {
        trackersDisabled[tracker] = true;
      } else {
        allTrackersDisabled = true;
      }
    };

    this.$get = [function () {
      return {
        "isEnabled": function isEnabled(tracker) {
          return !allTrackersDisabled && !trackersDisabled[tracker];
        }
      };
    }];
  }])

  // ================================================================================
  // Auto trackers
  // ================================================================================

  .run(['$ionicAutoTrack', '$ionicAnalytics', function ($ionicAutoTrack, $ionicAnalytics) {
    if (!$ionicAutoTrack.isEnabled('Load')) {
      return;
    }
    $ionicAnalytics.track('Load');
  }]).run(['$ionicAutoTrack', '$document', '$ionicAnalytics', 'domSerializer', function ($ionicAutoTrack, $document, $ionicAnalytics, domSerializer) {
    if (!$ionicAutoTrack.isEnabled('Tap')) {
      return;
    }

    $document.on('click', function (event) {
      // want coordinates as a percentage relative to the target element
      var box = event.target.getBoundingClientRect(),
          width = box.right - box.left,
          height = box.bottom - box.top,
          normX = (event.pageX - box.left) / width,
          normY = (event.pageY - box.top) / height;

      var eventData = {
        "coordinates": {
          "x": event.pageX,
          "y": event.pageY
        },
        "target": domSerializer.elementSelector(event.target),
        "target_identifier": domSerializer.elementName(event.target)
      };

      if (isFinite(normX) && isFinite(normY)) {
        eventData.coordinates.x_norm = normX; // eslint-disable-line
        eventData.coordinates.y_norm = normY; // eslint-disable-line
      }

      $ionicAnalytics.track('Tap', {
        "_ui": eventData
      });
    });
  }]).run(['$ionicAutoTrack', '$ionicAnalytics', '$rootScope', function ($ionicAutoTrack, $ionicAnalytics, $rootScope) {
    if (!$ionicAutoTrack.isEnabled('State Change')) {
      return;
    }

    $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
      // eslint-disable-line
      $ionicAnalytics.track('State Change', {
        "from": fromState.name,
        "to": toState.name
      });
    });
  }])

  // ================================================================================
  // ion-track-$EVENT
  // ================================================================================

  /**
   * @ngdoc directive
   * @name ionTrackClick
   * @module ionic.service.analytics
   * @restrict A
   * @parent ionic.directive:ionTrackClick
   *
   * @description
   *
   * A convenient directive to automatically track a click/tap on a button
   * or other tappable element.
   *
   * @usage
   * ```html
   * <button class="button button-clear" ion-track-click ion-track-event="cta-tap">Try now!</button>
   * ```
   */

  .directive('ionTrackClick', ionTrackDirective('click')).directive('ionTrackTap', ionTrackDirective('tap')).directive('ionTrackDoubletap', ionTrackDirective('doubletap')).directive('ionTrackHold', ionTrackDirective('hold')).directive('ionTrackRelease', ionTrackDirective('release')).directive('ionTrackDrag', ionTrackDirective('drag')).directive('ionTrackDragLeft', ionTrackDirective('dragleft')).directive('ionTrackDragRight', ionTrackDirective('dragright')).directive('ionTrackDragUp', ionTrackDirective('dragup')).directive('ionTrackDragDown', ionTrackDirective('dragdown')).directive('ionTrackSwipeLeft', ionTrackDirective('swipeleft')).directive('ionTrackSwipeRight', ionTrackDirective('swiperight')).directive('ionTrackSwipeUp', ionTrackDirective('swipeup')).directive('ionTrackSwipeDown', ionTrackDirective('swipedown')).directive('ionTrackTransform', ionTrackDirective('hold')).directive('ionTrackPinch', ionTrackDirective('pinch')).directive('ionTrackPinchIn', ionTrackDirective('pinchin')).directive('ionTrackPinchOut', ionTrackDirective('pinchout')).directive('ionTrackRotate', ionTrackDirective('rotate'));
}

},{}],7:[function(require,module,exports){
"use strict";

var _analytics = require("./analytics");

var _storage = require("./storage");

var _serializers = require("./serializers");

// Declare the window object
window.Ionic = window.Ionic || {};

// Ionic Namespace
Ionic.Analytics = _analytics.Analytics;

// Analytic Storage Namespace
Ionic.AnalyticStorage = {};
Ionic.AnalyticStorage.BucketStorage = _storage.BucketStorage;

// Analytic Serializers Namespace
Ionic.AnalyticSerializers = {};
Ionic.AnalyticSerializers.DOMSerializer = _serializers.DOMSerializer;

},{"./analytics":5,"./serializers":8,"./storage":9}],8:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var DOMSerializer = (function () {
  function DOMSerializer() {
    _classCallCheck(this, DOMSerializer);
  }

  _createClass(DOMSerializer, [{
    key: 'elementSelector',
    value: function elementSelector(element) {
      // iterate up the dom
      var selectors = [];
      while (element.tagName !== 'HTML') {
        var selector = element.tagName.toLowerCase();

        var id = element.getAttribute('id');
        if (id) {
          selector += "#" + id;
        }

        var className = element.className;
        if (className) {
          var classes = className.split(' ');
          for (var i = 0; i < classes.length; i++) {
            var c = classes[i];
            if (c) {
              selector += '.' + c;
            }
          }
        }

        if (!element.parentNode) {
          return null;
        }
        var childIndex = Array.prototype.indexOf.call(element.parentNode.children, element);
        selector += ':nth-child(' + (childIndex + 1) + ')';

        element = element.parentNode;
        selectors.push(selector);
      }

      return selectors.reverse().join('>');
    }
  }, {
    key: 'elementName',
    value: function elementName(element) {
      // 1. ion-track-name directive
      var name = element.getAttribute('ion-track-name');
      if (name) {
        return name;
      }

      // 2. id
      var id = element.getAttribute('id');
      if (id) {
        return id;
      }

      // 3. no unique identifier --> return null
      return null;
    }
  }]);

  return DOMSerializer;
})();

exports.DOMSerializer = DOMSerializer;

},{}],9:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _coreSettings = require("../core/settings");

var _coreCore = require("../core/core");

var settings = new _coreSettings.Settings();

var BucketStorage = (function () {
  function BucketStorage(name) {
    _classCallCheck(this, BucketStorage);

    this.name = name;
    this.baseStorage = _coreCore.IonicPlatform.getStorage();
  }

  _createClass(BucketStorage, [{
    key: "get",
    value: function get(key) {
      return this.baseStorage.retrieveObject(this.scopedKey(key));
    }
  }, {
    key: "set",
    value: function set(key, value) {
      return this.baseStorage.storeObject(this.scopedKey(key), value);
    }
  }, {
    key: "scopedKey",
    value: function scopedKey(key) {
      return this.name + '_' + key + '_' + settings.get('app_id');
    }
  }]);

  return BucketStorage;
})();

exports.BucketStorage = BucketStorage;

},{"../core/core":12,"../core/settings":19}],10:[function(require,module,exports){
// Add Angular integrations if Angular is available
'use strict';

if (typeof angular === 'object' && angular.module) {
  angular.module('ionic.service.core', [])

  /**
   * @private
   * Provides a safe interface to store objects in persistent memory
   */
  .provider('persistentStorage', function () {
    return {
      '$get': [function () {
        var storage = Ionic.getService('Storage');
        if (!storage) {
          storage = new Ionic.IO.Storage();
          Ionic.addService('Storage', storage, true);
        }
        return storage;
      }]
    };
  }).factory('$ionicCoreSettings', [function () {
    return new Ionic.IO.Settings();
  }]).factory('$ionicUser', [function () {
    return Ionic.User;
  }]).run([function () {
    Ionic.io();
  }]);
}

},{}],11:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _logger = require("./logger");

var privateData = {};

function privateVar(key) {
  return privateData[key] || null;
}

var App = (function () {
  function App(appId, apiKey) {
    _classCallCheck(this, App);

    this.logger = new _logger.Logger({
      'prefix': 'Ionic App:'
    });
    if (!appId || appId === '') {
      this.logger.info('No app_id was provided');
      return false;
    }

    if (!apiKey || apiKey === '') {
      this.logger.info('No api_key was provided');
      return false;
    }

    privateData.id = appId;
    privateData.apiKey = apiKey;

    // other config value reference
    this.devPush = null;
    this.gcmKey = null;
  }

  _createClass(App, [{
    key: 'toString',
    value: function toString() {
      return '<IonicApp [\'' + this.id + '\'>';
    }
  }, {
    key: 'id',
    get: function get() {
      return privateVar('id');
    }
  }, {
    key: 'apiKey',
    get: function get() {
      return privateVar('apiKey');
    }
  }]);

  return App;
})();

exports.App = App;

},{"./logger":16}],12:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _events = require("./events");

var _storage = require("./storage");

var _logger = require("./logger");

var eventEmitter = new _events.EventEmitter();
var mainStorage = new _storage.Storage();

var IonicPlatform = (function () {
  function IonicPlatform() {
    _classCallCheck(this, IonicPlatform);

    var self = this;
    this.logger = new _logger.Logger({
      'prefix': 'Ionic Core:'
    });
    this.logger.info('init');
    this._pluginsReady = false;
    this.emitter = IonicPlatform.getEmitter();

    this._bootstrap();

    if (self.cordovaPlatformUnknown) {
      self.logger.info('attempting to mock plugins');
      self._pluginsReady = true;
      self.emitter.emit('ionic_core:plugins_ready');
    } else {
      try {
        document.addEventListener("deviceready", function () {
          self.logger.info('plugins are ready');
          self._pluginsReady = true;
          self.emitter.emit('ionic_core:plugins_ready');
        }, false);
      } catch (e) {
        self.logger.info('unable to listen for cordova plugins to be ready');
      }
    }
  }

  _createClass(IonicPlatform, [{
    key: "_isCordovaAvailable",
    value: function _isCordovaAvailable() {
      var self = this;
      this.logger.info('searching for cordova.js');

      if (typeof cordova !== 'undefined') {
        this.logger.info('cordova.js has already been loaded');
        return true;
      }

      var scripts = document.getElementsByTagName('script');
      var len = scripts.length;
      for (var i = 0; i < len; i++) {
        var script = scripts[i].getAttribute('src');
        if (script) {
          var parts = script.split('/');
          var partsLength = 0;
          try {
            partsLength = parts.length;
            if (parts[partsLength - 1] === 'cordova.js') {
              self.logger.info('cordova.js has previously been included.');
              return true;
            }
          } catch (e) {
            self.logger.info('encountered error while testing for cordova.js presence, ' + e.toString());
          }
        }
      }

      return false;
    }
  }, {
    key: "loadCordova",
    value: function loadCordova() {
      var self = this;
      if (!this._isCordovaAvailable()) {
        var cordovaScript = document.createElement('script');
        var cordovaSrc = 'cordova.js';
        switch (IonicPlatform.getDeviceTypeByNavigator()) {
          case 'android':
            if (window.location.href.substring(0, 4) === "file") {
              cordovaSrc = 'file:///android_asset/www/cordova.js';
            }
            break;

          case 'ipad':
          case 'iphone':
            try {
              var resource = window.location.search.match(/cordova_js_bootstrap_resource=(.*?)(&|#|$)/i);
              if (resource) {
                cordovaSrc = decodeURI(resource[1]);
              }
            } catch (e) {
              self.logger.info('could not find cordova_js_bootstrap_resource query param');
              self.logger.info(e);
            }
            break;

          case 'unknown':
            self.cordovaPlatformUnknown = true;
            return false;

          default:
            break;
        }
        cordovaScript.setAttribute('src', cordovaSrc);
        document.head.appendChild(cordovaScript);
        self.logger.info('injecting cordova.js');
      }
    }

    /**
     * Determine the device type via the user agent string
     * @return {string} name of device platform or "unknown" if unable to identify the device
     */
  }, {
    key: "_bootstrap",

    /**
     * Bootstrap Ionic Core
     *
     * Handles the cordova.js bootstrap
     * @return {void}
     */
    value: function _bootstrap() {
      this.loadCordova();
    }
  }, {
    key: "onReady",

    /**
     * Fire a callback when core + plugins are ready. This will fire immediately if
     * the components have already become available.
     *
     * @param {function} callback function to fire off
     * @return {void}
     */
    value: function onReady(callback) {
      var self = this;
      if (this._pluginsReady) {
        callback(self);
      } else {
        self.emitter.on('ionic_core:plugins_ready', function () {
          callback(self);
        });
      }
    }
  }], [{
    key: "getEmitter",
    value: function getEmitter() {
      return eventEmitter;
    }
  }, {
    key: "getStorage",
    value: function getStorage() {
      return mainStorage;
    }
  }, {
    key: "getMain",
    value: function getMain() {
      if (typeof Ionic !== 'undefined') {
        if (Ionic.IO && Ionic.IO.main) {
          return Ionic.IO.main;
        }
      }
      return null;
    }
  }, {
    key: "getDeviceTypeByNavigator",
    value: function getDeviceTypeByNavigator() {
      var agent = navigator.userAgent;

      var ipad = agent.match(/iPad/i);
      if (ipad && ipad[0].toLowerCase() === 'ipad') {
        return 'ipad';
      }

      var iphone = agent.match(/iPhone/i);
      if (iphone && iphone[0].toLowerCase() === 'iphone') {
        return 'iphone';
      }

      var android = agent.match(/Android/i);
      if (android && android[0].toLowerCase() === 'android') {
        return 'android';
      }

      return "unknown";
    }

    /**
     * Check if the device is an Android device
     * @return {boolean} True if Android, false otherwise
     */
  }, {
    key: "isAndroidDevice",
    value: function isAndroidDevice() {
      var device = IonicPlatform.getDeviceTypeByNavigator();
      if (device === 'android') {
        return true;
      }
      return false;
    }

    /**
     * Check if the device is an iOS device
     * @return {boolean} True if iOS, false otherwise
     */
  }, {
    key: "isIOSDevice",
    value: function isIOSDevice() {
      var device = IonicPlatform.getDeviceTypeByNavigator();
      if (device === 'iphone' || device === 'ipad') {
        return true;
      }
      return false;
    }
  }, {
    key: "deviceConnectedToNetwork",
    value: function deviceConnectedToNetwork(strictMode) {
      if (typeof strictMode === 'undefined') {
        strictMode = false;
      }

      if (typeof navigator.connection === 'undefined' || typeof navigator.connection.type === 'undefined' || typeof Connection === 'undefined') {
        if (!strictMode) {
          return true;
        }
        return false;
      }

      switch (navigator.connection.type) {
        case Connection.ETHERNET:
        case Connection.WIFI:
        case Connection.CELL_2G:
        case Connection.CELL_3G:
        case Connection.CELL_4G:
        case Connection.CELL:
          return true;

        default:
          return false;
      }
    }
  }, {
    key: "Version",
    get: function get() {
      return '0.2.0';
    }
  }]);

  return IonicPlatform;
})();

exports.IonicPlatform = IonicPlatform;

},{"./events":15,"./logger":16,"./storage":20}],13:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var dataTypeMapping = {};

var DataTypeSchema = (function () {
  function DataTypeSchema(properties) {
    _classCallCheck(this, DataTypeSchema);

    this.data = {};
    this.setProperties(properties);
  }

  _createClass(DataTypeSchema, [{
    key: 'setProperties',
    value: function setProperties(properties) {
      if (properties instanceof Object) {
        for (var x in properties) {
          this.data[x] = properties[x];
        }
      }
    }
  }, {
    key: 'toJSON',
    value: function toJSON() {
      var data = this.data;
      return {
        '__Ionic_DataTypeSchema': data.name,
        'value': data.value
      };
    }
  }, {
    key: 'isValid',
    value: function isValid() {
      if (this.data.name && this.data.value) {
        return true;
      }
      return false;
    }
  }]);

  return DataTypeSchema;
})();

var DataType = (function () {
  function DataType() {
    _classCallCheck(this, DataType);
  }

  _createClass(DataType, null, [{
    key: 'get',
    value: function get(name, value) {
      if (dataTypeMapping[name]) {
        return new dataTypeMapping[name](value);
      }
      return false;
    }
  }, {
    key: 'getMapping',
    value: function getMapping() {
      return dataTypeMapping;
    }
  }, {
    key: 'register',
    value: function register(name, cls) {
      dataTypeMapping[name] = cls;
    }
  }, {
    key: 'Schema',
    get: function get() {
      return DataTypeSchema;
    }
  }]);

  return DataType;
})();

exports.DataType = DataType;

var UniqueArray = (function () {
  function UniqueArray(value) {
    _classCallCheck(this, UniqueArray);

    this.data = [];
    if (value instanceof Array) {
      for (var x in value) {
        this.push(value[x]);
      }
    }
  }

  _createClass(UniqueArray, [{
    key: 'toJSON',
    value: function toJSON() {
      var data = this.data;
      var schema = new DataTypeSchema({ 'name': 'UniqueArray', 'value': data });
      return schema.toJSON();
    }
  }, {
    key: 'push',
    value: function push(value) {
      if (this.data.indexOf(value) === -1) {
        this.data.push(value);
      }
    }
  }, {
    key: 'pull',
    value: function pull(value) {
      var index = this.data.indexOf(value);
      this.data.splice(index, 1);
    }
  }], [{
    key: 'fromStorage',
    value: function fromStorage(value) {
      return new UniqueArray(value);
    }
  }]);

  return UniqueArray;
})();

exports.UniqueArray = UniqueArray;

DataType.register('UniqueArray', UniqueArray);

},{}],14:[function(require,module,exports){
"use strict";

var _app = require("./app");

var _core = require("./core");

var _events = require("./events");

var _logger = require("./logger");

var _promise = require("./promise");

var _request = require("./request");

var _settings = require("./settings");

var _storage = require("./storage");

var _user = require("./user");

var _dataTypes = require("./data-types");

// Declare the window object
window.Ionic = window.Ionic || {};

// Ionic Namespace
Ionic.Core = _core.IonicPlatform;
Ionic.User = _user.User;

// DataType Namespace
Ionic.DataType = _dataTypes.DataType;
Ionic.DataTypes = _dataTypes.DataType.getMapping();

// IO Namespace
Ionic.IO = {};
Ionic.IO.App = _app.App;
Ionic.IO.EventEmitter = _events.EventEmitter;
Ionic.IO.Logger = _logger.Logger;
Ionic.IO.Promise = _promise.Promise;
Ionic.IO.DeferredPromise = _promise.DeferredPromise;
Ionic.IO.Request = _request.Request;
Ionic.IO.Response = _request.Response;
Ionic.IO.APIRequest = _request.APIRequest;
Ionic.IO.APIResponse = _request.APIResponse;
Ionic.IO.Storage = _storage.Storage;
Ionic.IO.Settings = _settings.Settings;

// Provider a single storage for services that have previously been registered
var serviceStorage = {};

Ionic.io = function () {
  if (typeof Ionic.IO.main === 'undefined') {
    Ionic.IO.main = new Ionic.Core();
  }
  return Ionic.IO.main;
};

Ionic.getService = function (name) {
  if (typeof serviceStorage[name] === 'undefined' || !serviceStorage[name]) {
    return false;
  }
  return serviceStorage[name];
};

Ionic.addService = function (name, service, force) {
  if (service && typeof serviceStorage[name] === 'undefined') {
    serviceStorage[name] = service;
  } else if (service && force) {
    serviceStorage[name] = service;
  }
};

Ionic.removeService = function (name) {
  if (typeof serviceStorage[name] !== 'undefined') {
    delete serviceStorage[name];
  }
};

// Kickstart Ionic Platform
Ionic.io();

},{"./app":11,"./core":12,"./data-types":13,"./events":15,"./logger":16,"./promise":17,"./request":18,"./settings":19,"./storage":20,"./user":21}],15:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _EventEmitter = require("events");

var EventEmitter = (function () {
  function EventEmitter() {
    _classCallCheck(this, EventEmitter);

    this._emitter = new _EventEmitter();
  }

  _createClass(EventEmitter, [{
    key: "on",
    value: function on(event, callback) {
      return this._emitter.on(event, callback);
    }
  }, {
    key: "emit",
    value: function emit(label, data) {
      return this._emitter.emit(label, data);
    }
  }]);

  return EventEmitter;
})();

exports.EventEmitter = EventEmitter;

},{"events":2}],16:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Logger = (function () {
  function Logger(opts) {
    _classCallCheck(this, Logger);

    var options = opts || {};
    this._silence = false;
    this._prefix = false;
    this._options = options;
    this._bootstrap();
  }

  _createClass(Logger, [{
    key: "_bootstrap",
    value: function _bootstrap() {
      if (this._options.prefix) {
        this._prefix = this._options.prefix;
      }
    }
  }, {
    key: "info",
    value: function info(data) {
      if (!this._silence) {
        if (this._prefix) {
          console.log(this._prefix, data);
        } else {
          console.log(data);
        }
      }
    }
  }, {
    key: "warn",
    value: function warn(data) {
      if (!this._silence) {
        if (this._prefix) {
          console.log(this._prefix, data);
        } else {
          console.log(data);
        }
      }
    }
  }, {
    key: "error",
    value: function error(data) {
      if (this._prefix) {
        console.error(this._prefix, data);
      } else {
        console.error(data);
      }
    }
  }]);

  return Logger;
})();

exports.Logger = Logger;

},{}],17:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ES6Promise = require("es6-promise").Promise;

var Promise = ES6Promise;

exports.Promise = Promise;

var DeferredPromise = (function () {
  function DeferredPromise() {
    _classCallCheck(this, DeferredPromise);

    var self = this;
    this._update = false;
    this.promise = new ES6Promise(function (resolve, reject) {
      self.resolve = resolve;
      self.reject = reject;
    });
    var originalThen = this.promise.then;
    this.promise.then = function (ok, fail, update) {
      self._update = update;
      return originalThen.call(self.promise, ok, fail);
    };
  }

  _createClass(DeferredPromise, [{
    key: "notify",
    value: function notify(value) {
      if (this._update && typeof this._update === 'function') {
        this._update(value);
      }
    }
  }]);

  return DeferredPromise;
})();

exports.DeferredPromise = DeferredPromise;

},{"es6-promise":4}],18:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _promise = require("./promise");

var request = require("browser-request");

var Request = function Request() {
  _classCallCheck(this, Request);
};

exports.Request = Request;

var Response = function Response() {
  _classCallCheck(this, Response);
};

exports.Response = Response;

var APIResponse = (function (_Response) {
  _inherits(APIResponse, _Response);

  function APIResponse() {
    _classCallCheck(this, APIResponse);

    _get(Object.getPrototypeOf(APIResponse.prototype), "constructor", this).call(this);
  }

  return APIResponse;
})(Response);

exports.APIResponse = APIResponse;

var APIRequest = (function (_Request) {
  _inherits(APIRequest, _Request);

  function APIRequest(options) {
    _classCallCheck(this, APIRequest);

    _get(Object.getPrototypeOf(APIRequest.prototype), "constructor", this).call(this);
    var requestInfo = {};
    var p = new _promise.Promise(function (resolve, reject) {
      request(options, function (err, response, result) {
        requestInfo._lastError = err;
        requestInfo._lastResponse = response;
        requestInfo._lastResult = result;
        if (err) {
          reject(err);
        } else {
          if (response.statusCode < 200 || response.statusCode >= 400) {
            var _err = new Error("Request Failed with status code of " + response.statusCode);
            reject(_err);
          } else {
            resolve({ 'response': response, 'payload': result });
          }
        }
      });
    });
    p.requestInfo = requestInfo;
    return p;
  }

  return APIRequest;
})(Request);

exports.APIRequest = APIRequest;

},{"./promise":17,"browser-request":1}],19:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BaseSettings = (function () {
  function BaseSettings() {
    _classCallCheck(this, BaseSettings);

    this._settings = null;
    return this;
  }

  _createClass(BaseSettings, [{
    key: "factory",
    value: function factory(name, func) {
      this._settings = func();
      return this;
    }
  }, {
    key: "get",
    value: function get(name) {
      return this._settings.get(name);
    }
  }, {
    key: "finish",
    value: function finish() {
      return this;
    }
  }]);

  return BaseSettings;
})();

var temp = new BaseSettings().factory('$ionicCoreSettings', function () {
  "IONIC_SETTINGS_STRING_START";var settings = {"dev_push":true}; return { get: function(setting) { if (settings[setting]) { return settings[setting]; } return null; } };"IONIC_SETTINGS_STRING_END";
}).finish();

var Settings = (function () {
  function Settings() {
    _classCallCheck(this, Settings);

    this._locations = {
      'api': 'https://apps.ionic.io',
      'push': 'https://push.ionic.io',
      'analytics': 'https://analytics.ionic.io',
      'deploy': 'https://apps.ionic.io'
    };
    this._devLocations = this.get('dev_locations');
    if (!this._devLocations) {
      this._devLocations = {};
    }
  }

  _createClass(Settings, [{
    key: "get",
    value: function get(name) {
      return temp.get(name);
    }
  }, {
    key: "getURL",
    value: function getURL(name) {
      if (this._devLocations[name]) {
        return this._devLocations[name];
      } else if (this._locations[name]) {
        return this._locations[name];
      } else {
        return null;
      }
    }
  }]);

  return Settings;
})();

exports.Settings = Settings;

},{}],20:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _promise = require("./promise");

var PlatformLocalStorageStrategy = (function () {
  function PlatformLocalStorageStrategy() {
    _classCallCheck(this, PlatformLocalStorageStrategy);
  }

  _createClass(PlatformLocalStorageStrategy, [{
    key: 'get',
    value: function get(key) {
      return window.localStorage.getItem(key);
    }
  }, {
    key: 'remove',
    value: function remove(key) {
      return window.localStorage.removeItem(key);
    }
  }, {
    key: 'set',
    value: function set(key, value) {
      return window.localStorage.setItem(key, value);
    }
  }]);

  return PlatformLocalStorageStrategy;
})();

var objectCache = {};
var memoryLocks = {};

var Storage = (function () {
  function Storage() {
    _classCallCheck(this, Storage);

    this.strategy = new PlatformLocalStorageStrategy();
  }

  /**
   * Stores an object in local storage under the given key
   * @param {string} key Name of the key to store values in
   * @param {object} object The object to store with the key
   * @return {void}
   */

  _createClass(Storage, [{
    key: 'storeObject',
    value: function storeObject(key, object) {
      // Convert object to JSON and store in localStorage
      var json = JSON.stringify(object);
      this.strategy.set(key, json);

      // Then store it in the object cache
      objectCache[key] = object;
    }
  }, {
    key: 'deleteObject',
    value: function deleteObject(key) {
      this.strategy.remove(key);
      delete objectCache[key];
    }

    /**
     * Either retrieves the cached copy of an object,
     * or the object itself from localStorage.
     * @param {string} key The name of the key to pull from
     * @return {mixed} Returns the previously stored Object or null
     */
  }, {
    key: 'retrieveObject',
    value: function retrieveObject(key) {
      // First check to see if it's the object cache
      var cached = objectCache[key];
      if (cached) {
        return cached;
      }

      // Deserialize the object from JSON
      var json = this.strategy.get(key);

      // null or undefined --> return null.
      if (json === null) {
        return null;
      }

      try {
        return JSON.parse(json);
      } catch (err) {
        return null;
      }
    }

    /**
     * Locks the async call represented by the given promise and lock key.
     * Only one asyncFunction given by the lockKey can be running at any time.
     *
     * @param {string} lockKey should be a string representing the name of this async call.
     *        This is required for persistence.
     * @param {function} asyncFunction Returns a promise of the async call.
     * @returns {Promise} A new promise, identical to the one returned by asyncFunction,
     *          but with two new errors: 'in_progress', and 'last_call_interrupted'.
     */
  }, {
    key: 'lockedAsyncCall',
    value: function lockedAsyncCall(lockKey, asyncFunction) {

      var self = this;
      var deferred = new _promise.DeferredPromise();

      // If the memory lock is set, error out.
      if (memoryLocks[lockKey]) {
        deferred.reject('in_progress');
        return deferred.promise;
      }

      // If there is a stored lock but no memory lock, flag a persistence error
      if (this.strategy.get(lockKey) === 'locked') {
        deferred.reject('last_call_interrupted');
        deferred.promise.then(null, function () {
          self.strategy.remove(lockKey);
        });
        return deferred.promise;
      }

      // Set stored and memory locks
      memoryLocks[lockKey] = true;
      self.strategy.set(lockKey, 'locked');

      // Perform the async operation
      asyncFunction().then(function (successData) {
        deferred.resolve(successData);

        // Remove stored and memory locks
        delete memoryLocks[lockKey];
        self.strategy.remove(lockKey);
      }, function (errorData) {
        deferred.reject(errorData);

        // Remove stored and memory locks
        delete memoryLocks[lockKey];
        self.strategy.remove(lockKey);
      }, function (notifyData) {
        deferred.notify(notifyData);
      });

      return deferred.promise;
    }
  }]);

  return Storage;
})();

exports.Storage = Storage;

},{"./promise":17}],21:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _request = require("./request");

var _promise = require("./promise");

var _settings = require("./settings");

var _core = require("./core");

var _storage = require("./storage");

var _logger = require("./logger");

var _pushPushToken = require("../push/push-token");

var _dataTypesJs = require("./data-types.js");

var Core = _core.IonicPlatform;
var AppUserContext = null;
var settings = new _settings.Settings();
var storage = new _storage.Storage();

var userAPIBase = settings.getURL('api') + '/api/v1/app/' + settings.get('app_id') + '/users';
var userAPIEndpoints = {
  'load': function load(userModel) {
    return userAPIBase + '/' + userModel.id;
  },
  'remove': function remove(userModel) {
    return userAPIBase + '/' + userModel.id;
  },
  'save': function save(userModel) {
    return userAPIBase + '/' + userModel.id;
  },
  'identify': function identify() {
    return userAPIBase + '/identify';
  },
  'addToken': function addToken() {
    return userAPIBase + '/pushUnique';
  }
};

var UserContext = (function () {
  function UserContext() {
    _classCallCheck(this, UserContext);
  }

  _createClass(UserContext, null, [{
    key: "delete",
    value: function _delete() {
      storage.deleteObject(UserContext.label);
    }
  }, {
    key: "store",
    value: function store() {
      storage.storeObject(UserContext.label, User.current());
    }
  }, {
    key: "getRawData",
    value: function getRawData() {
      return storage.retrieveObject(UserContext.label) || false;
    }
  }, {
    key: "load",
    value: function load() {
      var data = storage.retrieveObject(UserContext.label) || false;
      if (data) {
        return User.fromContext(data);
      }
      return false;
    }
  }, {
    key: "label",
    get: function get() {
      return "ionic_io_user_" + settings.get('app_id');
    }
  }]);

  return UserContext;
})();

var PushData = (function () {

  /**
   * Push Data Object
   *
   * Holds push data to use in conjunction with Ionic User models.
   * @constructor
   * @param {object} tokens Formatted token data
   */

  function PushData(tokens) {
    _classCallCheck(this, PushData);

    this.logger = new _logger.Logger({
      'prefix': 'Ionic Push Token:'
    });
    this.tokens = {
      'android': [],
      'ios': []
    };
    if (tokens && typeof tokens === 'object') {
      this.tokens = tokens;
    }
  }

  /**
   * Add a new token to the current list of tokens
   * Duplicates are not added, but still return as succesfully added.
   *
   * @param {ionic.io.push.Token} token Push Token
   * @return {boolean} False on error, otherwise true
   */

  _createClass(PushData, [{
    key: "addToken",
    value: function addToken(token) {
      var platform = null;

      if (typeof token === 'undefined' || !token || token === '') {
        this.logger.info('you need to pass a valid token to addToken()');
        return false;
      }

      if (token.token) {
        token = token.token;
      }

      // check if this is a dev token
      if (token.slice(0, 3) === 'DEV') {
        this.logger.info('dev tokens cannot be saved to a user as they are a temporary resource');
        return false;
      }

      if (Core.isAndroidDevice()) {
        platform = 'android';
      } else if (Core.isIOSDevice()) {
        platform = 'ios';
      }

      if (platform === null || !this.tokens.hasOwnProperty(platform)) {
        this.logger.info('cannot determine the token platform. Are you running on an Android or iOS device?');
        return false;
      }

      var platformTokens = this.tokens[platform];
      var hasToken = false;
      var testToken = null;

      for (testToken in platformTokens) {
        if (platformTokens[testToken] === token) {
          hasToken = true;
        }
      }
      if (!hasToken) {
        platformTokens.push(token);
      }

      return true;
    }

    /**
     * Remove the specified token if it exists in any platform token listing
     * If it does not exist, nothing is removed, but we will still return success
     *
     * @param {ionic.io.push.Token} token Push Token
     * @return {boolean} true
     */
  }, {
    key: "removeToken",
    value: function removeToken(token) {
      var x;
      for (x in this.tokens) {
        var platform = this.tokens[x];
        var testToken;
        for (testToken in platform) {
          if (platform[testToken] === token.token) {
            platform.splice(testToken, 1);
          }
        }
      }
      return true;
    }
  }]);

  return PushData;
})();

var UserData = (function () {
  function UserData(data) {
    _classCallCheck(this, UserData);

    this.data = {};
    if (typeof data === 'object') {
      this.data = data;
      this.deserializerDataTypes();
    }
  }

  _createClass(UserData, [{
    key: "deserializerDataTypes",
    value: function deserializerDataTypes() {
      for (var x in this.data) {
        // if we have an object, let's check for custom data types
        if (typeof this.data[x] === 'object') {
          // do we have a custom type?
          if (this.data[x].__Ionic_DataTypeSchema) {
            var name = this.data[x].__Ionic_DataTypeSchema;
            var mapping = _dataTypesJs.DataType.getMapping();
            if (mapping[name]) {
              // we have a custom type and a registered class, give the custom data type
              // from storage
              this.data[x] = mapping[name].fromStorage(this.data[x].value);
            }
          }
        }
      }
    }
  }, {
    key: "set",
    value: function set(key, value) {
      this.data[key] = value;
    }
  }, {
    key: "unset",
    value: function unset(key) {
      delete this.data[key];
    }
  }, {
    key: "get",
    value: function get(key, defaultValue) {
      if (this.data.hasOwnProperty(key)) {
        return this.data[key];
      } else {
        return defaultValue || null;
      }
    }
  }]);

  return UserData;
})();

var User = (function () {
  function User() {
    _classCallCheck(this, User);

    this.logger = new _logger.Logger({
      'prefix': 'Ionic User:'
    });
    this._blockLoad = false;
    this._blockSave = false;
    this._blockDelete = false;
    this._dirty = false;
    this._fresh = true;
    this._unset = {};
    this.push = new PushData();
    this.data = new UserData();
  }

  _createClass(User, [{
    key: "isDirty",
    value: function isDirty() {
      return this._dirty;
    }
  }, {
    key: "isFresh",
    value: function isFresh() {
      return this._fresh;
    }
  }, {
    key: "isValid",
    value: function isValid() {
      if (this.id) {
        return true;
      }
      return false;
    }
  }, {
    key: "getAPIFormat",
    value: function getAPIFormat() {
      var self = this;
      var data = this.data.data;
      data.user_id = this.id; // eslint-disable-line camelcase
      data._push = {
        'android_tokens': this.push.tokens.android,
        'ios_tokens': this.push.tokens.ios
      };

      if (!this.isFresh()) {
        return { 'update': data, 'remove': self._unset };
      }
      return data;
    }
  }, {
    key: "getFormat",
    value: function getFormat(format) {
      var self = this;
      var formatted = null;
      switch (format) {
        case 'api':
          formatted = self.getAPIFormat();
          break;
      }
      return formatted;
    }
  }, {
    key: "delete",
    value: function _delete() {
      var self = this;
      var deferred = new _promise.DeferredPromise();

      if (!self.isValid()) {
        return false;
      }

      if (!self._blockDelete) {
        self._blockDelete = true;
        self._delete();
        new _request.APIRequest({
          'uri': userAPIEndpoints.remove(this),
          'method': 'DELETE',
          'headers': {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          }
        }).then(function (result) {
          self._blockDelete = false;
          self.logger.info('deleted ' + self);
          deferred.resolve(result);
        }, function (error) {
          self._blockDelete = false;
          self.logger.error(error);
          deferred.reject(error);
        });
      } else {
        self.logger.info("a delete operation is already in progress for " + this + ".");
        deferred.reject(false);
      }

      return deferred.promise;
    }
  }, {
    key: "_store",
    value: function _store() {
      if (this === User.current()) {
        UserContext.store();
      }
    }
  }, {
    key: "_delete",
    value: function _delete() {
      if (this === User.current()) {
        UserContext["delete"]();
      }
    }
  }, {
    key: "save",
    value: function save() {
      var self = this;
      var deferred = new _promise.DeferredPromise();

      if (!self._blockSave) {
        self._blockSave = true;
        self._store();
        var saveURL = userAPIEndpoints.save(this);
        var saveMethod = 'PUT';
        if (self.isFresh()) {
          saveURL = userAPIEndpoints.identify(this);
          saveMethod = 'POST';
        }
        new _request.APIRequest({
          'uri': saveURL,
          'method': saveMethod,
          'headers': {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          'body': JSON.stringify(self.getFormat('api'))
        }).then(function (result) {
          self._dirty = false;
          if (!self.isFresh()) {
            self._unset = {};
          }
          self._fresh = false;
          self._blockSave = false;
          self.logger.info('saved user');
          deferred.resolve(result);
        }, function (error) {
          self._dirty = true;
          self._blockSave = false;
          self.logger.error(error);
          deferred.reject(error);
        });
      } else {
        self.logger.info("a save operation is already in progress for " + this + ".");
        deferred.reject(false);
      }

      return deferred.promise;
    }
  }, {
    key: "toString",
    value: function toString() {
      return '<IonicUser [\'' + this.id + '\']>';
    }
  }, {
    key: "addPushToken",
    value: function addPushToken(token) {
      return this.push.addToken(token);
    }
  }, {
    key: "removePushToken",
    value: function removePushToken(token) {
      if (!(token instanceof _pushPushToken.PushToken)) {
        token = new _pushPushToken.PushToken(token);
      }
      return this.push.removeToken(token);
    }
  }, {
    key: "set",
    value: function set(key, value) {
      delete this._unset[key];
      return this.data.set(key, value);
    }
  }, {
    key: "get",
    value: function get(key, defaultValue) {
      return this.data.get(key, defaultValue);
    }
  }, {
    key: "unset",
    value: function unset(key) {
      this._unset[key] = true;
      return this.data.unset(key);
    }
  }, {
    key: "id",
    set: function set(v) {
      if (v && typeof v === 'string' && v !== '') {
        this._id = v;
        return true;
      } else {
        return false;
      }
    },
    get: function get() {
      return this._id || null;
    }
  }], [{
    key: "current",
    value: function current(user) {
      if (user) {
        AppUserContext = user;
        UserContext.store();
        return AppUserContext;
      } else {
        if (!AppUserContext) {
          AppUserContext = UserContext.load();
        }
        if (!AppUserContext) {
          AppUserContext = new User();
        }
        return AppUserContext;
      }
    }
  }, {
    key: "fromContext",
    value: function fromContext(data) {
      var user = new User();
      user.id = data._id;
      user.data = new UserData(data.data.data);
      user.push = new PushData(data.push.tokens);
      user._fresh = data._fresh;
      user._dirty = data._dirty;
      return user;
    }
  }, {
    key: "load",
    value: function load(id) {
      var deferred = new _promise.DeferredPromise();

      var tempUser = new User();
      tempUser.id = id;

      if (!tempUser._blockLoad) {
        tempUser._blockLoad = true;
        new _request.APIRequest({
          'uri': userAPIEndpoints.load(tempUser),
          'method': 'GET',
          'json': true,
          'headers': {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          }
        }).then(function (result) {
          tempUser._blockLoad = false;
          tempUser.logger.info('loaded user');

          // set the custom data
          tempUser.data = new UserData(result.payload.custom_data);

          // set the push tokens
          if (result.payload._push && result.payload._push.android_tokens) {
            tempUser.push.tokens.android = result.payload._push.android_tokens;
          }
          if (result.payload._push && result.payload._push.ios_tokens) {
            tempUser.push.tokens.ios = result.payload._push.ios_tokens;
          }

          tempUser.image = result.payload.image;
          tempUser._fresh = false;

          deferred.resolve(tempUser);
        }, function (error) {
          tempUser._blockLoad = false;
          tempUser.logger.error(error);
          deferred.reject(error);
        });
      } else {
        tempUser.logger.info("a load operation is already in progress for " + this + ".");
        deferred.reject(false);
      }

      return deferred.promise;
    }
  }, {
    key: "anonymousId",
    value: function anonymousId() {
      // this is not guaranteed to be unique
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : r & 0x3 | 0x8; //eslint-disable-line
        return v.toString(16);
      });
    }
  }]);

  return User;
})();

exports.User = User;

},{"../push/push-token":29,"./core":12,"./data-types.js":13,"./logger":16,"./promise":17,"./request":18,"./settings":19,"./storage":20}],22:[function(require,module,exports){
// Add Angular integrations if Angular is available
'use strict';

if (typeof angular === 'object' && angular.module) {

  var IonicAngularDeploy = null;

  angular.module('ionic.service.deploy', []).factory('$ionicDeploy', [function () {
    if (!IonicAngularDeploy) {
      IonicAngularDeploy = new Ionic.Deploy();
    }
    return IonicAngularDeploy;
  }]);
}

},{}],23:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _coreSettings = require("../core/settings");

var _corePromise = require("../core/promise");

var _coreLogger = require("../core/logger");

var _coreCore = require("../core/core");

var _coreEvents = require("../core/events");

var settings = new _coreSettings.Settings();

var NO_PLUGIN = "IONIC_DEPLOY_MISSING_PLUGIN";
var INITIAL_DELAY = 1 * 5 * 1000;
var WATCH_INTERVAL = 1 * 60 * 1000;

var Deploy = (function () {

  /**
   * Ionic Deploy
   *
   * This is the main interface that talks with the Ionic Deploy Plugin to facilitate
   * checking, downloading, and loading an update to your app.
   *
   * Base Usage:
   *
   *    Ionic.io();
   *    var deploy = new Ionic.Deploy();
   *    deploy.check().then(null, null, function(hasUpdate) {
   *      deploy.update();
   *    });
   *
   * @constructor
   */

  function Deploy() {
    _classCallCheck(this, Deploy);

    var self = this;
    this.logger = new _coreLogger.Logger({
      'prefix': 'Ionic Deploy:'
    });
    this._plugin = false;
    this._isReady = false;
    this._channelTag = 'production';
    this._emitter = new _coreEvents.EventEmitter();
    this.logger.info("init");
    _coreCore.IonicPlatform.getMain().onReady(function () {
      self.initialize();
      self._isReady = true;
      self._emitter.emit('ionic_deploy:ready');
    });
  }

  /**
   * Fetch the Deploy Plugin
   *
   * If the plugin has not been set yet, attempt to fetch it, otherwise log
   * a message.
   *
   * @return {IonicDeploy} Returns the plugin or false
   */

  _createClass(Deploy, [{
    key: "_getPlugin",
    value: function _getPlugin() {
      if (this._plugin) {
        return this._plugin;
      }
      if (typeof IonicDeploy === 'undefined') {
        this.logger.info('plugin is not installed or has not loaded. Have you run `ionic plugin add ionic-plugin-deploy` yet?');
        return false;
      }
      this._plugin = IonicDeploy;
      return IonicDeploy;
    }

    /**
     * Initialize the Deploy Plugin
     * @return {void}
     */
  }, {
    key: "initialize",
    value: function initialize() {
      var self = this;
      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.init(settings.get('app_id'), settings.getURL('deploy'));
        }
      });
    }

    /**
     * Check for updates
     *
     * @return {Promise} Will resolve with true if an update is available, false otherwise. A string or
     *   error will be passed to reject() in the event of a failure.
     */
  }, {
    key: "check",
    value: function check() {
      var self = this;
      var deferred = new _corePromise.DeferredPromise();

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.check(settings.get('app_id'), self._channelTag, function (result) {
            if (result && result === "true") {
              self.logger.info('an update is available');
              deferred.resolve(true);
            } else {
              self.logger.info('no updates available');
              deferred.resolve(false);
            }
          }, function (error) {
            self.logger.error('encountered an error while checking for updates');
            deferred.reject(error);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Download and available update
     *
     * This should be used in conjunction with extract()
     * @return {Promise} The promise which will resolve with true/false or use
     *    notify to update the download progress.
     */
  }, {
    key: "download",
    value: function download() {
      var self = this;
      var deferred = new _corePromise.DeferredPromise();

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.download(settings.get('app_id'), function (result) {
            if (result !== 'true' && result !== 'false') {
              deferred.notify(result);
            } else {
              if (result === 'true') {
                self.logger.info("download complete");
              }
              deferred.resolve(result === 'true');
            }
          }, function (error) {
            deferred.reject(error);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Extract the last downloaded update
     *
     * This should be called after a download() successfully resolves.
     * @return {Promise} The promise which will resolve with true/false or use
     *                   notify to update the extraction progress.
     */
  }, {
    key: "extract",
    value: function extract() {
      var self = this;
      var deferred = new _corePromise.DeferredPromise();

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.extract(settings.get('app_id'), function (result) {
            if (result !== 'done') {
              deferred.notify(result);
            } else {
              if (result === 'true') {
                self.logger.info("extraction complete");
              }
              deferred.resolve(result);
            }
          }, function (error) {
            deferred.reject(error);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Load the latest deployed version
     * This is only necessary to call if you have manually downloaded and extracted
     * an update and wish to reload the app with the latest deploy. The latest deploy
     * will automatically be loaded when the app is started.
     *
     * @return {void}
     */
  }, {
    key: "load",
    value: function load() {
      var self = this;
      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.redirect(settings.get('app_id'));
        }
      });
    }

    /**
     * Watch constantly checks for updates, and triggers an
     * event when one is ready.
     * @param {object} options Watch configuration options
     * @return {Promise} returns a promise that will get a notify() callback when an update is available
     */
  }, {
    key: "watch",
    value: function watch(options) {
      var deferred = new _corePromise.DeferredPromise();
      var opts = options || {};
      var self = this;

      if (typeof opts.initialDelay === 'undefined') {
        opts.initialDelay = INITIAL_DELAY;
      }
      if (typeof opts.interval === 'undefined') {
        opts.interval = WATCH_INTERVAL;
      }

      function checkForUpdates() {
        self.check().then(function (hasUpdate) {
          if (hasUpdate) {
            deferred.notify(hasUpdate);
          }
        }, function (err) {
          self.logger.info('unable to check for updates, ', err);
        });

        // Check our timeout to make sure it wasn't cleared while we were waiting
        // for a server response
        if (this._checkTimeout) {
          this._checkTimeout = setTimeout(checkForUpdates.bind(self), opts.interval);
        }
      }

      // Check after an initial short deplay
      this._checkTimeout = setTimeout(checkForUpdates.bind(self), opts.initialDelay);

      return deferred.promise;
    }

    /**
     * Stop automatically looking for updates
     * @return {void}
     */
  }, {
    key: "unwatch",
    value: function unwatch() {
      clearTimeout(this._checkTimeout);
      this._checkTimeout = null;
    }

    /**
     * Information about the current deploy
     *
     * @return {Promise} The resolver will be passed an object that has key/value
     *    pairs pertaining to the currently deployed update.
     */
  }, {
    key: "info",
    value: function info() {
      var deferred = new _corePromise.DeferredPromise();
      var self = this;

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.info(settings.get('app_id'), function (result) {
            deferred.resolve(result);
          }, function (err) {
            deferred.reject(err);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * List the Deploy versions that have been installed on this device
     *
     * @return {Promise} The resolver will be passed an array of deploy uuids
     */
  }, {
    key: "getVersions",
    value: function getVersions() {
      var deferred = new _corePromise.DeferredPromise();
      var self = this;

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.getVersions(settings.get('app_id'), function (result) {
            deferred.resolve(result);
          }, function (err) {
            deferred.reject(err);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Remove an installed deploy on this device
     *
     * @param {string} uuid The deploy uuid you wish to remove from the device
     * @return {Promise} Standard resolve/reject resolution
     */
  }, {
    key: "deleteVersion",
    value: function deleteVersion(uuid) {
      var deferred = new _corePromise.DeferredPromise();
      var self = this;

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.deleteVersion(settings.get('app_id'), uuid, function (result) {
            deferred.resolve(result);
          }, function (err) {
            deferred.reject(err);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Fetches the metadata for a given deploy uuid. If no uuid is given, it will attempt
     * to grab the metadata for the most recently known update version.
     *
     * @param {string} uuid The deploy uuid you wish to grab metadata for, can be left blank to grab latest known update metadata
     * @return {Promise} Standard resolve/reject resolution
     */
  }, {
    key: "getMetadata",
    value: function getMetadata(uuid) {
      var deferred = new _corePromise.DeferredPromise();
      var self = this;

      this.onReady(function () {
        if (self._getPlugin()) {
          self._plugin.getMetadata(settings.get('app_id'), uuid, function (result) {
            deferred.resolve(result.metadata);
          }, function (err) {
            deferred.reject(err);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Set the deploy channel that should be checked for updatse
     * See http://docs.ionic.io/docs/deploy-channels for more information
     *
     * @param {string} channelTag The channel tag to use
     * @return {void}
     */
  }, {
    key: "setChannel",
    value: function setChannel(channelTag) {
      this._channelTag = channelTag;
    }

    /**
     * Update app with the latest deploy
     * @param {boolean} deferLoad Defer loading the applied update after the installation
     * @return {Promise} A promise result
     */
  }, {
    key: "update",
    value: function update(deferLoad) {
      var deferred = new _corePromise.DeferredPromise();
      var self = this;
      var deferLoading = false;

      if (typeof deferLoad !== 'undefined') {
        deferLoading = deferLoad;
      }

      this.onReady(function () {
        if (self._getPlugin()) {
          // Check for updates
          self.check().then(function (result) {
            if (result === true) {
              // There are updates, download them
              var downloadProgress = 0;
              self.download().then(function (result) {
                if (!result) {
                  deferred.reject("download error");
                }
                self.extract().then(function (result) {
                  if (!result) {
                    deferred.reject("extraction error");
                  }
                  if (!deferLoading) {
                    deferred.resolve(true);
                    self._plugin.redirect(settings.get('app_id'));
                  } else {
                    deferred.resolve(true);
                  }
                }, function (error) {
                  deferred.reject(error);
                }, function (update) {
                  var progress = downloadProgress + update / 2;
                  deferred.notify(progress);
                });
              }, function (error) {
                deferred.reject(error);
              }, function (update) {
                downloadProgress = update / 2;
                deferred.notify(downloadProgress);
              });
            } else {
              deferred.resolve(false);
            }
          }, function (error) {
            deferred.reject(error);
          });
        } else {
          deferred.reject(NO_PLUGIN);
        }
      });

      return deferred.promise;
    }

    /**
     * Fire a callback when deploy is ready. This will fire immediately if
     * deploy has already become available.
     *
     * @param {Function} callback Callback function to fire off
     * @return {void}
     */
  }, {
    key: "onReady",
    value: function onReady(callback) {
      var self = this;
      if (this._isReady) {
        callback(self);
      } else {
        self._emitter.on('ionic_deploy:ready', function () {
          callback(self);
        });
      }
    }
  }]);

  return Deploy;
})();

exports.Deploy = Deploy;

},{"../core/core":12,"../core/events":15,"../core/logger":16,"../core/promise":17,"../core/settings":19}],24:[function(require,module,exports){
"use strict";

var _deploy = require("./deploy");

// Declare the window object
window.Ionic = window.Ionic || {};

// Ionic Namespace
Ionic.Deploy = _deploy.Deploy;

},{"./deploy":23}],25:[function(require,module,exports){
// Add Angular integrations if Angular is available
'use strict';

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

if (typeof angular === 'object' && angular.module) {

  var IonicAngularPush = null;

  angular.module('ionic.service.push', [])

  /**
   * IonicPushAction Service
   *
   * A utility service to kick off misc features as part of the Ionic Push service
   */
  .factory('$ionicPushAction', ['$state', function ($state) {
    var PushActionService = (function () {
      function PushActionService() {
        _classCallCheck(this, PushActionService);
      }

      _createClass(PushActionService, [{
        key: 'notificationNavigation',

        /**
         * State Navigation
         *
         * Attempts to navigate to a new view if a push notification payload contains:
         *
         *   - $state {String} The state name (e.g 'tab.chats')
         *   - $stateParams {Object} Provided state (url) params
         *
         * Find more info about state navigation and params:
         * https://github.com/angular-ui/ui-router/wiki
         *
         * @param {object} notification Notification Object
         * @return {void}
         */
        value: function notificationNavigation(notification) {
          var state = false;
          var stateParams = {};

          try {
            state = notification.additionalData.payload.$state;
          } catch (e) {
            state = false;
          }

          try {
            stateParams = JSON.parse(notification.additionalData.payload.$stateParams);
          } catch (e) {
            stateParams = {};
          }

          if (state) {
            $state.go(state, stateParams);
          }
        }
      }]);

      return PushActionService;
    })();

    return new PushActionService();
  }]).factory('$ionicPush', [function () {
    if (!IonicAngularPush) {
      IonicAngularPush = new Ionic.Push("DEFER_INIT");
    }
    return IonicAngularPush;
  }]).run(['$ionicPush', '$ionicPushAction', function ($ionicPush, $ionicPushAction) {
    // This is what kicks off the state redirection when a push notificaiton has the relevant details
    $ionicPush._emitter.on('ionic_push:processNotification', function (notification) {
      if (notification && notification.additionalData && notification.additionalData.foreground === false) {
        $ionicPushAction.notificationNavigation(notification);
      }
    });
  }]);
}

},{}],26:[function(require,module,exports){
"use strict";

var _push = require("./push");

var _pushToken = require("./push-token");

// Declare the window object
window.Ionic = window.Ionic || {};

// Ionic Namespace
Ionic.Push = _push.Push;
Ionic.PushToken = _pushToken.PushToken;

},{"./push":30,"./push-token":29}],27:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _coreRequest = require("../core/request");

var _coreSettings = require("../core/settings");

var _coreLogger = require("../core/logger");

var _pushToken = require("./push-token");

var settings = new _coreSettings.Settings();

/**
 * PushDev Service
 *
 * This service acts as a mock push service that is intended to be used pre-setup of
 * GCM/APNS in an Ionic.io project.
 *
 * How it works:
 *
 *   When register() is called, this service is used to generate a random
 *   development device token. This token is not valid for any service outside of
 *   Ionic Push with `dev_push` set to true. These tokens do not last long and are not
 *   eligible for use in a production app.
 *
 *   The device will then periodically check the Push service for push notifications sent
 *   to our development token -- so unlike a typical "push" update, this actually uses
 *   "polling" to find new notifications. This means you *MUST* have the application open
 *   and in the foreground to retreive messsages.
 *
 *   The callbacks provided in your init() will still be triggered as normal,
 *   but with these notable exceptions:
 *
 *      - There is no payload data available with messages
 *      - An alert() is called when a notification is received unlesss you return false
 *        in your 'onNotification' callback.
 *
 */

var PushDevService = (function () {
  function PushDevService() {
    _classCallCheck(this, PushDevService);

    this.logger = new _coreLogger.Logger({
      'prefix': 'Ionic Push (dev):'
    });
    this._serviceHost = settings.getURL('push');
    this._token = false;
    this._watch = false;
  }

  /**
   * Generate a development token
   *
   * @return {String} development device token
   */

  _createClass(PushDevService, [{
    key: "getDevToken",
    value: function getDevToken() {
      // Some crazy bit-twiddling to generate a random guid
      var token = 'DEV-xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
            v = c === 'x' ? r : r & 0x3 | 0x8;
        return v.toString(16);
      });
      this._token = token;
      return this._token;
    }

    /**
     * Registers a development token with the Ionic Push service
     *
     * @param {IonicPushService} ionicPush Instantiated Push Service
     * @param {function} callback Registration Callback
     * @return {void}
     */
  }, {
    key: "init",
    value: function init(ionicPush, callback) {
      this._push = ionicPush;
      this._emitter = this._push._emitter;
      var token = this._token;
      var self = this;
      if (!token) {
        token = this.getDevToken();
      }

      var requestOptions = {
        "method": 'POST',
        "uri": this._serviceHost + '/dev/push',
        'headers': {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        "body": JSON.stringify({
          "dev_token": token
        })
      };

      new _coreRequest.APIRequest(requestOptions).then(function () {
        var data = { "registrationId": token };
        self.logger.info('registered with development push service', token);
        self._emitter.emit("ionic_push:token", data);
        if (typeof callback === 'function') {
          callback(new _pushToken.PushToken(self._token));
        }
        self.watch();
      }, function (error) {
        self.logger.error("error connecting development push service.", error);
      });
    }

    /**
     * Checks the push service for notifications that target the current development token
     * @return {void}
     */
  }, {
    key: "checkForNotifications",
    value: function checkForNotifications() {
      if (!this._token) {
        return false;
      }

      var self = this;
      var requestOptions = {
        'method': 'GET',
        'uri': this._serviceHost + '/dev/push/check',
        'headers': {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'X-Ionic-Dev-Token': this._token
        },
        'json': true
      };

      new _coreRequest.APIRequest(requestOptions).then(function (result) {
        if (result.payload.messages.length > 0) {
          var message = {
            'message': result.payload.messages[0],
            'title': 'DEVELOPMENT PUSH'
          };

          self.logger.warn("Ionic Push: Development Push received. Development pushes will not contain payload data.");
          self._emitter.emit("ionic_push:notification", message);
        }
      }, function (error) {
        self.logger.error("unable to check for development pushes.", error);
      });
    }

    /**
     * Kicks off the "polling" of the Ionic Push service for new push notifications
     * @return {void}
     */
  }, {
    key: "watch",
    value: function watch() {
      // Check for new dev pushes every 5 seconds
      this.logger.info('watching for new notifications');
      var self = this;
      if (!this._watch) {
        this._watch = setInterval(function () {
          self.checkForNotifications();
        }, 5000);
      }
    }

    /**
     * Puts the "polling" for new notifications on hold.
     * @return {void}
     */
  }, {
    key: "halt",
    value: function halt() {
      if (this._watch) {
        clearInterval(this._watch);
      }
    }
  }]);

  return PushDevService;
})();

exports.PushDevService = PushDevService;

},{"../core/logger":16,"../core/request":18,"../core/settings":19,"./push-token":29}],28:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var PushMessageAppStatus = (function () {
  function PushMessageAppStatus() {
    _classCallCheck(this, PushMessageAppStatus);

    this.asleep = false;
    this.closed = false;
  }

  _createClass(PushMessageAppStatus, [{
    key: 'wasAsleep',
    get: function get() {
      return this.asleep;
    }
  }, {
    key: 'wasClosed',
    get: function get() {
      return this.closed;
    }
  }]);

  return PushMessageAppStatus;
})();

var PushMessage = (function () {
  function PushMessage(raw) {
    _classCallCheck(this, PushMessage);

    this._raw = raw || {};

    if (!this._raw.additionalData) {
      // this should only hit if we are serving up a development push
      this._raw.additionalData = {
        'coldstart': false,
        'foreground': true
      };
    }

    this._payload = null;
    this.app = null;
    this.text = null;
    this.title = null;
    this.count = null;
    this.sound = null;
    this.image = null;
  }

  _createClass(PushMessage, [{
    key: 'processRaw',
    value: function processRaw() {
      this.text = this._raw.message || null;
      this.title = this._raw.title || null;
      this.count = this._raw.count || null;
      this.sound = this._raw.sound || null;
      this.image = this._raw.image || null;
      this.app = new PushMessageAppStatus();

      if (!this._raw.additionalData.foreground) {
        this.app.asleep = true;
      }

      if (this._raw.additionalData.coldstart) {
        this.app.closed = true;
      }

      if (this._raw.additionalData.payload) {
        this._payload = this._raw.additionalData.payload;
      }
    }
  }, {
    key: 'getRawVersion',
    value: function getRawVersion() {
      return this._raw;
    }
  }, {
    key: 'toString',
    value: function toString() {
      return '<PushMessage [\'' + this.title + '\']>';
    }
  }, {
    key: 'payload',
    get: function get() {
      return this._payload || {};
    }
  }], [{
    key: 'fromPluginJSON',
    value: function fromPluginJSON(json) {
      var message = new PushMessage(json);
      message.processRaw();
      return message;
    }
  }]);

  return PushMessage;
})();

exports.PushMessage = PushMessage;

},{}],29:[function(require,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var PushToken = (function () {
  function PushToken(token) {
    _classCallCheck(this, PushToken);

    this._token = token || null;
  }

  _createClass(PushToken, [{
    key: 'toString',
    value: function toString() {
      var token = this._token || 'null';
      return '<PushToken [\'' + token + '\']>';
    }
  }, {
    key: 'token',
    set: function set(value) {
      this._token = value;
    },
    get: function get() {
      return this._token;
    }
  }]);

  return PushToken;
})();

exports.PushToken = PushToken;

},{}],30:[function(require,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var _coreApp = require("../core/app");

var _coreSettings = require("../core/settings");

var _coreCore = require("../core/core");

var _coreLogger = require("../core/logger");

var _coreEvents = require("../core/events");

var _coreRequest = require("../core/request");

var _corePromise = require("../core/promise");

var _pushToken = require("./push-token");

var _pushMessage = require("./push-message");

var _pushDev = require("./push-dev");

var settings = new _coreSettings.Settings();

var DEFER_INIT = "DEFER_INIT";

var pushAPIBase = settings.getURL('api') + '/api/v1/app/' + settings.get('app_id') + '/push';
var pushAPIEndpoints = {
  'invalidateToken': function invalidateToken() {
    return pushAPIBase + '/invalidate-token';
  }
};

/**
 * Push Service
 *
 * This is the main entrypoint for interacting with the Ionic Push service.
 * Example Usage:
 *
 *   Ionic.io(); // kick off the io platform
 *   var push = new Ionic.Push({
 *     "debug": true,
 *     "onNotification": function(notification) {
 *       var payload = $ionicPush.getPayload(notification);
 *       console.log(notification, payload);
 *     },
 *     "onRegister": function(data) {
 *       console.log(data);
 *     }
 *   });
 *
 *   // Registers for a device token using the options passed to init()
 *   push.register(callback);
 *
 *   // Unregister the current registered token
 *   push.unregister();
 *
 */

var Push = (function () {
  function Push(config) {
    _classCallCheck(this, Push);

    this.logger = new _coreLogger.Logger({
      'prefix': 'Ionic Push:'
    });

    var IonicApp = new _coreApp.App(settings.get('app_id'), settings.get('api_key'));
    IonicApp.devPush = settings.get('dev_push');
    IonicApp.gcmKey = settings.get('gcm_key');

    // Check for the required values to use this service
    if (!IonicApp.id || !IonicApp.apiKey) {
      this.logger.error('no app_id or api_key found. (http://docs.ionic.io/docs/io-install)');
      return false;
    } else if (_coreCore.IonicPlatform.isAndroidDevice() && !IonicApp.devPush && !IonicApp.gcmKey) {
      this.logger.error('GCM project number not found (http://docs.ionic.io/docs/push-android-setup)');
      return false;
    }

    this.app = IonicApp;
    this.registerCallback = false;
    this.notificationCallback = false;
    this.errorCallback = false;
    this._token = false;
    this._notification = false;
    this._debug = false;
    this._isReady = false;
    this._tokenReady = false;
    this._blockRegistration = false;
    this._registered = false;
    this._emitter = new _coreEvents.EventEmitter();
    if (config !== DEFER_INIT) {
      var self = this;
      _coreCore.IonicPlatform.getMain().onReady(function () {
        self.init(config);
      });
    }
  }

  _createClass(Push, [{
    key: "getStorageToken",
    value: function getStorageToken() {
      var storage = _coreCore.IonicPlatform.getStorage();
      var token = storage.retrieveObject('ionic_io_push_token');
      if (token) {
        return new _pushToken.PushToken(token.token);
      }
      return null;
    }
  }, {
    key: "clearStorageToken",
    value: function clearStorageToken() {
      var storage = _coreCore.IonicPlatform.getStorage();
      storage.deleteObject('ionic_io_push_token');
    }

    /**
     * Init method to setup push behavior/options
     *
     * The config supports the following properties:
     *   - debug {Boolean} Enables some extra logging as well as some default callback handlers
     *   - onNotification {Function} Callback function that is passed the notification object
     *   - onRegister {Function} Callback function that is passed the registration object
     *   - onError {Function} Callback function that is passed the error object
     *   - pluginConfig {Object} Plugin configuration: https://github.com/phonegap/phonegap-plugin-push
     *
     * @param {object} config Configuration object
     * @return {Push} returns the called Push instantiation
     */
  }, {
    key: "init",
    value: function init(config) {
      this._getPushPlugin();
      if (typeof config === 'undefined') {
        config = {};
      }
      if (typeof config !== 'object') {
        this.logger.error('init() requires a valid config object.');
        return false;
      }
      var self = this;

      if (!config.pluginConfig) {
        config.pluginConfig = {};
      }

      if (_coreCore.IonicPlatform.isAndroidDevice()) {
        // inject gcm key for PushPlugin
        if (!config.pluginConfig.android) {
          config.pluginConfig.android = {};
        }
        if (!config.pluginConfig.android.senderId) {
          config.pluginConfig.android.senderID = self.app.gcmKey;
        }
      }

      // Store Callbacks
      if (config.onRegister) {
        this.setRegisterCallback(config.onRegister);
      }
      if (config.onNotification) {
        this.setNotificationCallback(config.onNotification);
      }
      if (config.onError) {
        this.setErrorCallback(config.onError);
      }

      this._config = config;
      this._isReady = true;

      this._emitter.emit('ionic_push:ready', { "config": this._config });
      return this;
    }

    /**
     * Store the currently registered device token with a User
     *
     * @param {IonicUser} user The User the token should be associated with
     * @return {void}
     */
  }, {
    key: "addTokenToUser",
    value: function addTokenToUser(user) {
      if (!this._token) {
        this.logger.info('a token must be registered before you can add it to a user.');
      }
      if (typeof user === 'object') {
        if (_coreCore.IonicPlatform.isAndroidDevice()) {
          user.addPushToken(this._token, 'android');
        } else if (_coreCore.IonicPlatform.isIOSDevice()) {
          user.addPushToken(this._token, 'ios');
        } else {
          this.logger.info('token is not a valid Android or iOS registration id. Cannot save to user.');
        }
      } else {
        this.logger.info('invalid $ionicUser object passed to $ionicPush.addToUser()');
      }
    }

    /**
     * Registers the device with GCM/APNS to get a device token
     * Fires off the 'onRegister' callback if one has been provided in the init() config
     * @param {function} callback Callback Function
     * @return {void}
     */
  }, {
    key: "register",
    value: function register(callback) {
      this.logger.info('register');
      var self = this;
      if (this._blockRegistration) {
        self.logger.info("another registration is already in progress.");
        return false;
      }
      this._blockRegistration = true;
      this.onReady(function () {
        if (self.app.devPush) {
          var IonicDevPush = new _pushDev.PushDevService();
          self._debugCallbackRegistration();
          self._callbackRegistration();
          IonicDevPush.init(self, callback);
          self._blockRegistration = false;
          self._tokenReady = true;
        } else {
          self._plugin = PushNotification.init(self._config.pluginConfig);
          self._plugin.on('registration', function (data) {
            self._blockRegistration = false;
            self.token = new _pushToken.PushToken(data.registrationId);
            self._tokenReady = true;
            if (typeof callback === 'function') {
              callback(self._token);
            }
          });
          self._debugCallbackRegistration();
          self._callbackRegistration();
        }
        self._registered = true;
      });
    }

    /**
     * Invalidate the current GCM/APNS token
     *
     * @return {Promise} the unregister result
     */
  }, {
    key: "unregister",
    value: function unregister() {
      var self = this;
      var deferred = new _corePromise.DeferredPromise();
      var platform = null;

      if (_coreCore.IonicPlatform.isAndroidDevice()) {
        platform = 'android';
      } else if (_coreCore.IonicPlatform.isIOSDevice()) {
        platform = 'ios';
      }

      if (!platform) {
        deferred.reject("Could not detect the platform, are you on a device?");
      }

      if (!self._blockUnregister) {
        if (this._plugin) {
          this._plugin.unregister(function () {}, function () {});
        }
        new _coreRequest.APIRequest({
          'uri': pushAPIEndpoints.invalidateToken(self),
          'method': 'POST',
          'headers': {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          'body': JSON.stringify({
            'platform': platform,
            'token': self.getStorageToken().token
          })
        }).then(function (result) {
          self._blockUnregister = false;
          self.logger.info('unregistered push token: ' + self.getStorageToken().token);
          self.clearStorageToken();
          deferred.resolve(result);
        }, function (error) {
          self._blockUnregister = false;
          self.logger.error(error);
          deferred.reject(error);
        });
      } else {
        self.logger.info("an unregister operation is already in progress.");
        deferred.reject(false);
      }

      return deferred.promise;
    }

    /**
     * Convenience method to grab the payload object from a notification
     *
     * @param {PushNotification} notification Push Notification object
     * @return {object} Payload object or an empty object
     */
  }, {
    key: "getPayload",
    value: function getPayload(notification) {
      return notification.payload;
    }

    /**
     * Set the registration callback
     *
     * @param {function} callback Registration callback function
     * @return {boolean} true if set correctly, otherwise false
     */
  }, {
    key: "setRegisterCallback",
    value: function setRegisterCallback(callback) {
      if (typeof callback !== 'function') {
        this.logger.info('setRegisterCallback() requires a valid callback function');
        return false;
      }
      this.registerCallback = callback;
      return true;
    }

    /**
     * Set the notification callback
     *
     * @param {function} callback Notification callback function
     * @return {boolean} true if set correctly, otherwise false
     */
  }, {
    key: "setNotificationCallback",
    value: function setNotificationCallback(callback) {
      if (typeof callback !== 'function') {
        this.logger.info('setNotificationCallback() requires a valid callback function');
        return false;
      }
      this.notificationCallback = callback;
      return true;
    }

    /**
     * Set the error callback
     *
     * @param {function} callback Error callback function
     * @return {boolean} true if set correctly, otherwise false
     */
  }, {
    key: "setErrorCallback",
    value: function setErrorCallback(callback) {
      if (typeof callback !== 'function') {
        this.logger.info('setErrorCallback() requires a valid callback function');
        return false;
      }
      this.errorCallback = callback;
      return true;
    }
  }, {
    key: "_debugRegistrationCallback",
    value: function _debugRegistrationCallback() {
      var self = this;
      function callback(data) {
        self.token = new _pushToken.PushToken(data.registrationId);
        self.logger.info('(debug) device token registered: ' + self._token);
      }
      return callback;
    }
  }, {
    key: "_debugNotificationCallback",
    value: function _debugNotificationCallback() {
      var self = this;
      function callback(notification) {
        self._processNotification(notification);
        var message = _pushMessage.PushMessage.fromPluginJSON(notification);
        self.logger.info('(debug) notification received: ' + message);
        if (!self.notificationCallback && self.app.devPush) {
          alert(message.text);
        }
      }
      return callback;
    }
  }, {
    key: "_debugErrorCallback",
    value: function _debugErrorCallback() {
      var self = this;
      function callback(err) {
        self.logger.error('(debug) unexpected error occured.');
        self.logger.error(err);
      }
      return callback;
    }
  }, {
    key: "_registerCallback",
    value: function _registerCallback() {
      var self = this;
      function callback(data) {
        self.token = new _pushToken.PushToken(data.registrationId);
        if (self.registerCallback) {
          return self.registerCallback(self._token);
        }
      }
      return callback;
    }
  }, {
    key: "_notificationCallback",
    value: function _notificationCallback() {
      var self = this;
      function callback(notification) {
        self._processNotification(notification);
        var message = _pushMessage.PushMessage.fromPluginJSON(notification);
        if (self.notificationCallback) {
          return self.notificationCallback(message);
        }
      }
      return callback;
    }
  }, {
    key: "_errorCallback",
    value: function _errorCallback() {
      var self = this;
      function callback(err) {
        if (self.errorCallback) {
          return self.errorCallback(err);
        }
      }
      return callback;
    }

    /**
     * Registers the default debug callbacks with the PushPlugin when debug is enabled
     * Internal Method
     * @private
     * @return {void}
     */
  }, {
    key: "_debugCallbackRegistration",
    value: function _debugCallbackRegistration() {
      if (this._config.debug) {
        if (!this.app.devPush) {
          this._plugin.on('registration', this._debugRegistrationCallback());
          this._plugin.on('notification', this._debugNotificationCallback());
          this._plugin.on('error', this._debugErrorCallback());
        } else {
          if (!this._registered) {
            this._emitter.on('ionic_push:token', this._debugRegistrationCallback());
            this._emitter.on('ionic_push:notification', this._debugNotificationCallback());
            this._emitter.on('ionic_push:error', this._debugErrorCallback());
          }
        }
      }
    }

    /**
     * Registers the user supplied callbacks with the PushPlugin
     * Internal Method
     * @return {void}
     */
  }, {
    key: "_callbackRegistration",
    value: function _callbackRegistration() {
      if (!this.app.devPush) {
        this._plugin.on('registration', this._registerCallback());
        this._plugin.on('notification', this._notificationCallback());
        this._plugin.on('error', this._errorCallback());
      } else {
        if (!this._registered) {
          this._emitter.on('ionic_push:token', this._registerCallback());
          this._emitter.on('ionic_push:notification', this._notificationCallback());
          this._emitter.on('ionic_push:error', this._errorCallback());
        }
      }
    }

    /**
     * Performs misc features based on the contents of a push notification
     * Internal Method
     *
     * Currently just does the payload $state redirection
     * @param {PushNotification} notification Push Notification object
     * @return {void}
     */
  }, {
    key: "_processNotification",
    value: function _processNotification(notification) {
      this._notification = notification;
      this._emitter.emit('ionic_push:processNotification', notification);
    }

    /**
     * Fetch the phonegap-push-plugin interface
     * Internal Method
     *
     * @return {PushNotification} PushNotification instance
     */
  }, {
    key: "_getPushPlugin",
    value: function _getPushPlugin() {
      var self = this;
      var PushPlugin = false;
      try {
        PushPlugin = window.PushNotification;
      } catch (e) {
        self.logger.info('something went wrong looking for the PushNotification plugin');
      }

      if (!self.app.devPush && !PushPlugin && (_coreCore.IonicPlatform.isIOSDevice() || _coreCore.IonicPlatform.isAndroidDevice())) {
        self.logger.error("PushNotification plugin is required. Have you run `ionic plugin add phonegap-plugin-push` ?");
      }
      return PushPlugin;
    }

    /**
     * Fire a callback when Push is ready. This will fire immediately if
     * the service has already initialized.
     *
     * @param {function} callback Callback function to fire off
     * @return {void}
     */
  }, {
    key: "onReady",
    value: function onReady(callback) {
      var self = this;
      if (this._isReady) {
        callback(self);
      } else {
        self._emitter.on('ionic_push:ready', function () {
          callback(self);
        });
      }
    }
  }, {
    key: "token",
    set: function set(val) {
      var storage = _coreCore.IonicPlatform.getStorage();
      if (val instanceof _pushToken.PushToken) {
        storage.storeObject('ionic_io_push_token', { 'token': val.token });
      }
      this._token = val;
    }
  }]);

  return Push;
})();

exports.Push = Push;

},{"../core/app":11,"../core/core":12,"../core/events":15,"../core/logger":16,"../core/promise":17,"../core/request":18,"../core/settings":19,"./push-dev":27,"./push-message":28,"./push-token":29}]},{},[17,18,15,16,20,19,13,12,21,11,14,10,29,28,27,30,26,25,23,24,22,9,8,5,7,6]);
