/*!
 * vum.bundle.js is a concatenation of:
 * vum.js, angular.js, angular-animate.js,
 * angular-sanitize.js, angular-ui-router.js,
 * and vum-angular.js
 */

/*!
 * Copyright 2015 Drifty Co.
 * http://drifty.com/
 *
 * vum, v1.3.0
 * A powerful HTML5 mobile app framework.
 * http://vumframework.com/
 *
 * By @maxlynch, @benjsperry, @adamdbradley <3
 *
 * Licensed under the MIT license. Please see LICENSE for more information.
 *
 */
/* eslint-disable */
(function () {
// Create global vum obj and its namespaces
// build processes may have already created an vum obj
  window.vum = window.vum || {}
  window.vum.version = '1.3.0';

  (function (window, document, vum) {
    var readyCallbacks = []
    var isDomReady = document.readyState === 'complete' || document.readyState === 'interactive'

    function domReady () {
      isDomReady = true
      for (var x = 0; x < readyCallbacks.length; x++) {
        vum.requestAnimationFrame(readyCallbacks[x])
      }
      readyCallbacks = []
      document.removeEventListener('DOMContentLoaded', domReady)
    }

    if (!isDomReady) {
      document.addEventListener('DOMContentLoaded', domReady)
    }
  })(window, document, vum);

  (function (window, document, vum) {
    function getParameterByName (name) {
      name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
      var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
        results = regex.exec(location.search)
      return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '))
    }

    var IOS = 'ios'
    var ANDROID = 'android'
    var WINDOWS_PHONE = 'windowsphone'
    var EDGE = 'edge'
    var CROSSWALK = 'crosswalk'
    var requestAnimationFrame = requestAnimationFrame // eslint-disable-line
    // From the man himself, Mr. Paul Irish.
    // The requestAnimationFrame polyfill
    // Put it on window just to preserve its context
    // without having to use .call
    window._rAF = (function () {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        function (callback) {
          window.setTimeout(callback, 16)
        }
    })()

    function requestAnimationFrame (cb) {
      return window._rAF(cb)
    }

    /**
     * @ngdoc utility
     * @name vum.Platform
     * @module vum
     * @description
     * A set of utility methods that can be used to retrieve the device ready state and
     * various other information such as what kind of platform the app is currently installed on.
     *
     * @usage
     * ```js
     * angular.module('PlatformApp', ['vum'])
     * .controller('PlatformCtrl', function($scope) {
   *
   *   vum.Platform.ready(function(){
   *     // will execute when device is ready, or immediately if the device is already ready.
   *   });
   *
   *   var deviceInformation = vum.Platform.device();
   *
   *   var isWebView = vum.Platform.isWebView();
   *   var isIPad = vum.Platform.isIPad();
   *   var isIOS = vum.Platform.isIOS();
   *   var isAndroid = vum.Platform.isAndroid();
   *   var isWindowsPhone = vum.Platform.isWindowsPhone();
   *
   *   var currentPlatform = vum.Platform.platform();
   *   var currentPlatformVersion = vum.Platform.version();
   *
   *   vum.Platform.exitApp(); // stops the app
   * });
     * ```
     */
    var self = vum.Platform = {

      // Put navigator on platform so it can be mocked and set
      // the browser does not allow window.navigator to be set
      navigator: window.navigator,

      /**
       * @ngdoc property
       * @name vum.Platform#isReady
       * @returns {boolean} Whether the device is ready.
       */
      isReady: false,
      /**
       * @ngdoc property
       * @name vum.Platform#isFullScreen
       * @returns {boolean} Whether the device is fullscreen.
       */
      isFullScreen: false,
      /**
       * @ngdoc property
       * @name vum.Platform#platforms
       * @returns {Array(string)} An array of all platforms found.
       */
      platforms: null,
      /**
       * @ngdoc property
       * @name vum.Platform#grade
       * @returns {string} What grade the current platform is.
       */
      grade: null,
      /**
       * @ngdoc property
       * @name vum.Platform#ua
       * @returns {string} What User Agent is.
       */
      ua: navigator.userAgent,

      /**
       * @ngdoc method
       * @name vum.Platform#ready
       * @description
       * Trigger a callback once the device is ready, or immediately
       * if the device is already ready. This method can be run from
       * anywhere and does not need to be wrapped by any additonal methods.
       * When the app is within a WebView (Cordova), it'll fire
       * the callback once the device is ready. If the app is within
       * a web browser, it'll fire the callback after `window.load`.
       * Please remember that Cordova features (Camera, FileSystem, etc) still
       * will not work in a web browser.
       * @param {function} callback The function to call.
       */
      ready: function (cb) {
        // run through tasks to complete now that the device is ready
        if (self.isReady) {
          cb()
        } else {
          // the platform isn't ready yet, add it to this array
          // which will be called once the platform is ready
          readyCallbacks.push(cb)
        }
      },

      /**
       * @private
       */
      detect: function () {
        self._checkPlatforms()

        requestAnimationFrame(function () {
          // only add to the body class if we got platform info
          for (var i = 0; i < self.platforms.length; i++) {
            document.body.classList.add('platform-' + self.platforms[i])
          }
        })
      },

      /**
       * @ngdoc method
       * @name vum.Platform#setGrade
       * @description Set the grade of the device: 'a', 'b', or 'c'. 'a' is the best
       * (most css features enabled), 'c' is the worst.  By default, sets the grade
       * depending on the current device.
       * @param {string} grade The new grade to set.
       */
      setGrade: function (grade) {
        var oldGrade = self.grade
        self.grade = grade
        requestAnimationFrame(function () {
          if (oldGrade) {
            document.body.classList.remove('grade-' + oldGrade)
          }
          document.body.classList.add('grade-' + grade)
        })
      },

      /**
       * @ngdoc method
       * @name vum.Platform#device
       * @description Return the current device (given by cordova).
       * @returns {object} The device object.
       */
      device: function () {
        return window.device || {}
      },

      _checkPlatforms: function () {
        self.platforms = []
        var grade = 'a'

        if (self.isWebView()) {
          self.platforms.push('webview')
          if (!(!window.cordova && !window.PhoneGap && !window.phonegap)) {
            self.platforms.push('cordova')
          } else if (typeof window.forge === 'object') {
            self.platforms.push('trigger')
          }
        } else {
          self.platforms.push('browser')
        }
        if (self.isIPad()) self.platforms.push('ipad')

        var platform = self.platform()
        if (platform) {
          self.platforms.push(platform)

          var version = self.version()
          if (version) {
            var v = version.toString()
            if (v.indexOf('.') > 0) {
              v = v.replace('.', '_')
            } else {
              v += '_0'
            }
            self.platforms.push(platform + v.split('_')[0])
            self.platforms.push(platform + v)

            if (self.isAndroid() && version < 4.4) {
              grade = (version < 4 ? 'c' : 'b')
            } else if (self.isWindowsPhone()) {
              grade = 'b'
            }
          }
        }

        self.setGrade(grade)
      },

      /**
       * @ngdoc method
       * @name vum.Platform#isWebView
       * @returns {boolean} Check if we are running within a WebView (such as Cordova).
       */
      isWebView: function () {
        return !(!window.cordova && !window.PhoneGap && !window.phonegap && window.forge !== 'object')
      },
      /**
       * @ngdoc method
       * @name vum.Platform#isIPad
       * @returns {boolean} Whether we are running on iPad.
       */
      isIPad: function () {
        if (/iPad/i.test(self.navigator.platform)) {
          return true
        }
        return /iPad/i.test(self.ua)
      },
      /**
       * @ngdoc method
       * @name vum.Platform#isIOS
       * @returns {boolean} Whether we are running on iOS.
       */
      isIOS: function () {
        return self.is(IOS)
      },
      /**
       * @ngdoc method
       * @name vum.Platform#isAndroid
       * @returns {boolean} Whether we are running on Android.
       */
      isAndroid: function () {
        return self.is(ANDROID)
      },
      /**
       * @ngdoc method
       * @name vum.Platform#isWindowsPhone
       * @returns {boolean} Whether we are running on Windows Phone.
       */
      isWindowsPhone: function () {
        return self.is(WINDOWS_PHONE)
      },
      /**
       * @ngdoc method
       * @name vum.Platform#isEdge
       * @returns {boolean} Whether we are running on MS Edge/Windows 10 (inc. Phone)
       */
      isEdge: function () {
        return self.is(EDGE)
      },

      isCrosswalk: function () {
        return self.is(CROSSWALK)
      },

      /**
       * @ngdoc method
       * @name vum.Platform#platform
       * @returns {string} The name of the current platform.
       */
      platform: function () {
        // singleton to get the platform name
        if (platformName === null) self.setPlatform(self.device().platform)
        return platformName
      },

      /**
       * @private
       */
      setPlatform: function (n) {
        if (typeof n !== 'undefined' && n !== null && n.length) {
          platformName = n.toLowerCase()
        } else if (getParameterByName('vumplatform')) {
          platformName = getParameterByName('vumplatform')
        } else if (self.ua.indexOf('Edge') > -1) {
          platformName = EDGE
        } else if (self.ua.indexOf('Windows Phone') > -1) {
          platformName = WINDOWS_PHONE
        } else if (self.ua.indexOf('Android') > 0) {
          platformName = ANDROID
        } else if (/iPhone|iPad|iPod/.test(self.ua)) {
          platformName = IOS
        } else {
          platformName = self.navigator.platform && navigator.platform.toLowerCase().split(' ')[0] || ''
        }
      },

      /**
       * @ngdoc method
       * @name vum.Platform#version
       * @returns {number} The version of the current device platform.
       */
      version: function () {
        // singleton to get the platform version
        if (platformVersion === null) self.setVersion(self.device().version)
        return platformVersion
      },

      /**
       * @private
       */
      setVersion: function (v) {
        if (typeof v !== 'undefined' && v !== null) {
          v = v.split('.')
          v = parseFloat(v[0] + '.' + (v.length > 1 ? v[1] : 0))
          if (!isNaN(v)) {
            platformVersion = v
            return
          }
        }

        platformVersion = 0

        // fallback to user-agent checking
        var pName = self.platform()
        var versionMatch = {
          'android': /Android (\d+).(\d+)?/,
          'ios': /OS (\d+)_(\d+)?/,
          'windowsphone': /Windows Phone (\d+).(\d+)?/,
        }
        if (versionMatch[pName]) {
          v = self.ua.match(versionMatch[pName])
          if (v && v.length > 2) {
            platformVersion = parseFloat(v[1] + '.' + v[2])
          }
        }
      },

      /**
       * @ngdoc method
       * @name vum.Platform#is
       * @param {string} Platform name.
       * @returns {boolean} Whether the platform name provided is detected.
       */
      is: function (type) {
        type = type.toLowerCase()
        // check if it has an array of platforms
        if (self.platforms) {
          for (var x = 0; x < self.platforms.length; x++) {
            if (self.platforms[x] === type) return true
          }
        }
        // exact match
        var pName = self.platform()
        if (pName) {
          return pName === type.toLowerCase()
        }

        // A quick hack for to check userAgent
        return self.ua.toLowerCase().indexOf(type) >= 0
      },

      /**
       * @ngdoc method
       * @name vum.Platform#exitApp
       * @description Exit the app.
       */
      exitApp: function () {
        self.ready(function () {
          navigator.app && navigator.app.exitApp && navigator.app.exitApp()
        })
      },

      /**
       * @ngdoc method
       * @name vum.Platform#showStatusBar
       * @description Shows or hides the device status bar (in Cordova). Requires `cordova plugin add org.apache.cordova.statusbar`
       * @param {boolean} shouldShow Whether or not to show the status bar.
       */
      showStatusBar: function (val) {
        // Only useful when run within cordova
        self._showStatusBar = val
        self.ready(function () {
          // run this only when or if the platform (cordova) is ready
          requestAnimationFrame(function () {
            if (self._showStatusBar) {
              // they do not want it to be full screen
              window.StatusBar && window.StatusBar.show()
              document.body.classList.remove('status-bar-hide')
            } else {
              // it should be full screen
              window.StatusBar && window.StatusBar.hide()
              document.body.classList.add('status-bar-hide')
            }
          })
        })
      },

      /**
       * @ngdoc method
       * @name vum.Platform#fullScreen
       * @description
       * Sets whether the app is fullscreen or not (in Cordova).
       * @param {boolean=} showFullScreen Whether or not to set the app to fullscreen. Defaults to true. Requires `cordova plugin add org.apache.cordova.statusbar`
       * @param {boolean=} showStatusBar Whether or not to show the device's status bar. Defaults to false.
       */
      fullScreen: function (showFullScreen, showStatusBar) {
        // showFullScreen: default is true if no param provided
        self.isFullScreen = (showFullScreen !== false)

        // add/remove the fullscreen classname to the body
        vum.DomUtil.ready(function () {
          // run this only when or if the DOM is ready
          requestAnimationFrame(function () {
            if (self.isFullScreen) {
              document.body.classList.add('fullscreen')
            } else {
              document.body.classList.remove('fullscreen')
            }
          })
          // showStatusBar: default is false if no param provided
          self.showStatusBar((showStatusBar === true))
        })
      },

    }

    var platformName = null, // just the name, like iOS or Android
      platformVersion = null, // a float of the major and minor, like 7.1
      readyCallbacks = [],
      windowLoadListenderAttached,
      platformReadyTimer = 2000 // How long to wait for platform ready before emitting a warning

    verifyPlatformReady()

    // Warn the user if deviceready did not fire in a reasonable amount of time, and how to fix it.
    function verifyPlatformReady () {
      setTimeout(function () {
        if (!self.isReady && self.isWebView()) {
          void 0
        }
      }, platformReadyTimer)
    }

    // setup listeners to know when the device is ready to go
    function onWindowLoad () {
      if (self.isWebView()) {
        // the window and scripts are fully loaded, and a cordova/phonegap
        // object exists then let's listen for the deviceready
        document.addEventListener('deviceready', onPlatformReady, false)
      } else {
        // the window and scripts are fully loaded, but the window object doesn't have the
        // cordova/phonegap object, so its just a browser, not a webview wrapped w/ cordova
        onPlatformReady()
      }
      if (windowLoadListenderAttached) {
        window.removeEventListener('load', onWindowLoad, false)
      }
    }

    if (document.readyState === 'complete') {
      onWindowLoad()
    } else {
      windowLoadListenderAttached = true
      window.addEventListener('load', onWindowLoad, false)
    }

    function onPlatformReady () {
      // the device is all set to go, init our own stuff then fire off our event
      self.isReady = true
      self.detect()
      for (var x = 0; x < readyCallbacks.length; x++) {
        // fire off all the callbacks that were added before the platform was ready
        readyCallbacks[x]()
      }
      readyCallbacks = []
      vum.trigger('platformready', {target: document})

      requestAnimationFrame(function () {
        document.body.classList.add('platform-ready')
      })
    }

    // document.addEventListener('')
  })(window, document, vum);
  /**
   * ion-events.js
   *
   * Author: Max Lynch <max@drifty.com>
   *
   * Framework events handles various mobile browser events, and
   * detects special events like tap/swipe/etc. and emits them
   * as custom events that can be used in an app.
   *
   * Portions lovingly adapted from github.com/maker/ratchet and github.com/alexgibson/tap.js - thanks guys!
   */

  (function (vum) {
    // Custom event polyfill
    vum.CustomEvent = (function () {
      if (typeof window.CustomEvent === 'function') return CustomEvent

      var customEvent = function (event, params) {
        var evt
        params = params || {
          bubbles: false,
          cancelable: false,
          detail: undefined,
        }
        try {
          evt = document.createEvent('CustomEvent')
          evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
        } catch (error) {
          // fallback for browsers that don't support createEvent('CustomEvent')
          evt = document.createEvent('Event')
          for (var param in params) {
            evt[param] = params[param]
          }
          evt.initEvent(event, params.bubbles, params.cancelable)
        }
        return evt
      }
      customEvent.prototype = window.Event.prototype
      return customEvent
    })()

    /**
     * @ngdoc utility
     * @name vum.EventController
     * @module vum
     */
    vum.EventController = {
      VIRTUALIZED_EVENTS: ['tap', 'swipe', 'swiperight', 'swipeleft', 'drag', 'hold', 'release'],

      /**
       * @ngdoc method
       * @name vum.EventController#trigger
       * @alias vum.trigger
       * @param {string} eventType The event to trigger.
       * @param {object} data The data for the event. Hint: pass in
       * `{target: targetElement}`
       * @param {boolean=} bubbles Whether the event should bubble up the DOM.
       * @param {boolean=} cancelable Whether the event should be cancelable.
       */
      // Trigger a new event
      trigger: function (eventType, data, bubbles, cancelable) {
        var event = new vum.CustomEvent(eventType, {
          detail: data,
          bubbles: !!bubbles,
          cancelable: !!cancelable,
        })

        // Make sure to trigger the event on the given target, or dispatch it from
        // the window if we don't have an event target
        data && data.target && data.target.dispatchEvent && data.target.dispatchEvent(event) || window.dispatchEvent(event)
      },

      /**
       * @ngdoc method
       * @name vum.EventController#on
       * @alias vum.on
       * @description Listen to an event on an element.
       * @param {string} type The event to listen for.
       * @param {function} callback The listener to be called.
       * @param {DOMElement} element The element to listen for the event on.
       */
      on: function (type, callback, element) {
        var e = element || window

        // Bind a gesture if it's a virtual event
        for (var i = 0, j = this.VIRTUALIZED_EVENTS.length; i < j; i++) {
          if (type == this.VIRTUALIZED_EVENTS[i]) {
            var gesture = new vum.Gesture(element)
            gesture.on(type, callback)
            return gesture
          }
        }

        // Otherwise bind a normal event
        e.addEventListener(type, callback)
      },

      /**
       * @ngdoc method
       * @name vum.EventController#off
       * @alias vum.off
       * @description Remove an event listener.
       * @param {string} type
       * @param {function} callback
       * @param {DOMElement} element
       */
      off: function (type, callback, element) {
        element.removeEventListener(type, callback)
      },

      /**
       * @ngdoc method
       * @name vum.EventController#onGesture
       * @alias vum.onGesture
       * @description Add an event listener for a gesture on an element.
       *
       * Available eventTypes (from [hammer.js](http://eightmedia.github.io/hammer.js/)):
       *
       * `hold`, `tap`, `doubletap`, `drag`, `dragstart`, `dragend`, `dragup`, `dragdown`, <br/>
       * `dragleft`, `dragright`, `swipe`, `swipeup`, `swipedown`, `swipeleft`, `swiperight`, <br/>
       * `transform`, `transformstart`, `transformend`, `rotate`, `pinch`, `pinchin`, `pinchout`, <br/>
       * `touch`, `release`
       *
       * @param {string} eventType The gesture event to listen for.
       * @param {function(e)} callback The function to call when the gesture
       * happens.
       * @param {DOMElement} element The angular element to listen for the event on.
       * @param {object} options object.
       * @returns {vum.Gesture} The gesture object (use this to remove the gesture later on).
       */
      onGesture: function (type, callback, element, options) {
        var gesture = new vum.Gesture(element, options)
        gesture.on(type, callback)
        return gesture
      },

      /**
       * @ngdoc method
       * @name vum.EventController#offGesture
       * @alias vum.offGesture
       * @description Remove an event listener for a gesture created on an element.
       * @param {vum.Gesture} gesture The gesture that should be removed.
       * @param {string} eventType The gesture event to remove the listener for.
       * @param {function(e)} callback The listener to remove.

       */
      offGesture: function (gesture, type, callback) {
        gesture && gesture.off(type, callback)
      },

      handlePopState: function () {
      },
    }

    // Map some convenient top-level functions for event handling
    vum.on = function () {
      vum.EventController.on.apply(vum.EventController, arguments)
    }
    vum.off = function () {
      vum.EventController.off.apply(vum.EventController, arguments)
    }
    vum.trigger = vum.EventController.trigger// function() { vum.EventController.trigger.apply(vum.EventController.trigger, arguments); };
    vum.onGesture = function () {
      return vum.EventController.onGesture.apply(vum.EventController.onGesture, arguments)
    }
    vum.offGesture = function () {
      return vum.EventController.offGesture.apply(vum.EventController.offGesture, arguments)
    }
  })(window.vum);

  (function (vum) {
    vum.$vumPlatform = {

      /**
       * @ngdoc method
       * @name $vumPlatform#onHardwareBackButton
       * @description
       * Some platforms have a hardware back button, so this is one way to
       * bind to it.
       * @param {function} callback the callback to trigger when this event occurs
       */
      onHardwareBackButton: function (cb) {
        vum.Platform.ready(function () {
          document.addEventListener('backbutton', cb, false)
        })
      },

      /**
       * @ngdoc method
       * @name $vumPlatform#offHardwareBackButton
       * @description
       * Remove an event listener for the backbutton.
       * @param {function} callback The listener function that was
       * originally bound.
       */
      offHardwareBackButton: function (fn) {
        vum.Platform.ready(function () {
          document.removeEventListener('backbutton', fn)
        })
      },

      /**
       * @ngdoc method
       * @name $vumPlatform#registerBackButtonAction
       * @description
       * Register a hardware back button action. Only one action will execute
       * when the back button is clicked, so this method decides which of
       * the registered back button actions has the highest priority.
       *
       * For example, if an actionsheet is showing, the back button should
       * close the actionsheet, but it should not also go back a page view
       * or close a modal which may be open.
       *
       * The priorities for the existing back button hooks are as follows:
       *   Return to previous view = 100
       *   Close side menu = 150
       *   Dismiss modal = 200
       *   Close action sheet = 300
       *   Dismiss popup = 400
       *   Dismiss loading overlay = 500
       *
       * Your back button action will override each of the above actions
       * whose priority is less than the priority you provide. For example,
       * an action assigned a priority of 101 will override the 'return to
       * previous view' action, but not any of the other actions.
       *
       * @param {function} callback Called when the back button is pressed,
       * if this listener is the highest priority.
       * @param {number} priority Only the highest priority will execute.
       * @param {*=} actionId The id to assign this action. Default: a
       * random unique id.
       * @returns {function} A function that, when called, will deregister
       * this backButtonAction.
       */
      $backButtonActions: {},
      registerBackButtonAction: function (fn, priority, actionId) {
        if (!vum.$vumPlatform._hasBackButtonHandler) {
          // add a back button listener if one hasn't been setup yet
          vum.$vumPlatform.$backButtonActions = {}
          vum.$vumPlatform.onHardwareBackButton(vum.$vumPlatform.hardwareBackButtonClick)
          vum.$vumPlatform._hasBackButtonHandler = true
        }

        var action = {
          id: (actionId || vum.Utils.nextUid()),
          priority: (priority || 0),
          fn: fn,
        }
        vum.$vumPlatform.$backButtonActions[action.id] = action

        // return a function to de-register this back button action
        return function () {
          delete vum.$vumPlatform.$backButtonActions[action.id]
        }
      },

      /**
       * @private
       */
      hardwareBackButtonClick: function (e) {
        // loop through all the registered back button actions
        // and only run the last one of the highest priority
        var priorityAction, actionId
        for (actionId in vum.$vumPlatform.$backButtonActions) {
          if (!priorityAction || vum.$vumPlatform.$backButtonActions[actionId].priority >= priorityAction.priority) {
            priorityAction = vum.$vumPlatform.$backButtonActions[actionId]
          }
        }
        if (priorityAction) {
          priorityAction.fn(e)
          return priorityAction
        }
      },

      is: function (type) {
        return vum.Platform.is(type)
      },

      /**
       * @ngdoc method
       * @name $vumPlatform#on
       * @description
       * Add Cordova event listeners, such as `pause`, `resume`, `volumedownbutton`, `batterylow`,
       * `offline`, etc. More information about available event types can be found in
       * [Cordova's event documentation](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events).
       * @param {string} type Cordova [event type](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events).
       * @param {function} callback Called when the Cordova event is fired.
       * @returns {function} Returns a deregistration function to remove the event listener.
       */
      on: function (type, cb) {
        vum.Platform.ready(function () {
          document.addEventListener(type, cb, false)
        })
        return function () {
          vum.Platform.ready(function () {
            document.removeEventListener(type, cb)
          })
        }
      },

      /**
       * @ngdoc method
       * @name $vumPlatform#ready
       * @description
       * Trigger a callback once the device is ready,
       * or immediately if the device is already ready.
       * @param {function=} callback The function to call.
       */
      ready: function (cb) {
        vum.Platform.ready(function () {
          cb()
        })
      },

      getWinSize: function () {
        let winWidth
        let winHeight
        // 获取窗口宽度
        if (window.innerWidth) { winWidth = window.innerWidth } else if ((document.body) && (document.body.clientWidth))
        // 获取窗口高度
        { winWidth = document.body.clientWidth }
        if (window.innerHeight) { winHeight = window.innerHeight } else if ((document.body) && (document.body.clientHeight)) { winHeight = document.body.clientHeight }
        // 通过深入Document内部对body进行检测，获取窗口大小
        if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) {
          winHeight = document.documentElement.clientHeight
          winWidth = document.documentElement.clientWidth
        }
        return {'width': winWidth, 'height': winHeight}
      },

    }

    return vum.$vumPlatform
  })(window.vum);

  (function () {
    var nextId = 0
    vum.Utils = {
      nextUid: function () {
        return 'vue' + (nextId++)
      },
    }

    var jqLite // delay binding since jQuery could be loaded after us.
    var hasOwnProperty = Object.prototype.hasOwnProperty

    /**
     * @ngdoc function
     * @name angular.isUndefined
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is undefined.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is undefined.
     */
    vum.isUndefined = function (value) {
      return typeof value === 'undefined'
    }

    /**
     * @ngdoc function
     * @name angular.isDefined
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is defined.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is defined.
     */
    vum.isDefined = function (value) {
      return typeof value !== 'undefined'
    }

    /**
     * @ngdoc function
     * @name angular.isObject
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
     * considered to be objects. Note that JavaScript arrays are objects.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is an `Object` but not `null`.
     */
    vum.isObject = function (value) {
      // http://jsperf.com/isobject4
      return value !== null && typeof value === 'object'
    }

    /**
     * Determine if a value is an object with a null prototype
     *
     * @returns {boolean} True if `value` is an `Object` with a null prototype
     */
    vum.isBlankObject = function (value) {
      return value !== null && typeof value === 'object' && !getPrototypeOf(value)
    }

    /**
     * @ngdoc function
     * @name angular.isString
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is a `String`.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is a `String`.
     */
    vum.isString = function (value) {
      return typeof value === 'string'
    }

    /**
     * @ngdoc function
     * @name angular.isNumber
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is a `Number`.
     *
     * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
     *
     * If you wish to exclude these then you can use the native
     * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
     * method.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is a `Number`.
     */
    vum.isNumber = function (value) {
      return typeof value === 'number'
    }

    /**
     * @ngdoc function
     * @name angular.isDate
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a value is a date.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is a `Date`.
     */
    vum.isDate = function (value) {
      return toString.call(value) === '[object Date]'
    }

    /**
     * @ngdoc function
     * @name angular.isArray
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is an `Array`.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is an `Array`.
     */
    var isArray = Array.isArray

    /**
     * @ngdoc function
     * @name angular.isFunction
     * @module ng
     * @kind function
     *
     * @description
     * Determines if a reference is a `Function`.
     *
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is a `Function`.
     */
    vum.isFunction = function (value) {
      return typeof value === 'function'
    }

    /**
     * Determines if a value is a regular expression object.
     *
     * @private
     * @param {*} value Reference to check.
     * @returns {boolean} True if `value` is a `RegExp`.
     */
    vum.isRegExp = function (value) {
      return toString.call(value) === '[object RegExp]'
    }

    /**
     * Checks if `obj` is a window object.
     *
     * @private
     * @param {*} obj Object to check
     * @returns {boolean} True if `obj` is a window obj.
     */
    vum.isWindow = function (obj) {
      return obj && obj.window === obj
    }

    vum.isScope = function (obj) {
      return obj && obj.$evalAsync && obj.$watch
    }

    vum.isFile = function (obj) {
      return toString.call(obj) === '[object File]'
    }

    vum.isFormData = function (obj) {
      return toString.call(obj) === '[object FormData]'
    }

    vum.isBlob = function (obj) {
      return toString.call(obj) === '[object Blob]'
    }

    vum.isBoolean = function (value) {
      return typeof value === 'boolean'
    }

    vum.isPromiseLike = function (obj) {
      return obj && isFunction(obj.then)
    }

    var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/

    vum.isTypedArray = function (value) {
      return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value))
    }

    vum.isArrayBuffer = function (obj) {
      return toString.call(obj) === '[object ArrayBuffer]'
    }

    vum.toJsonReplacer = function (key, value) {
      var val = value

      if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
        val = undefined
      } else if (vum.isWindow(value)) {
        val = '$WINDOW'
      } else if (value && document === value) {
        val = '$DOCUMENT'
      } else if (vum.isScope(value)) {
        val = '$SCOPE'
      }

      return val
    }

    /**
     * @ngdoc function
     * @name angular.toJson
     * @module ng
     * @kind function
     *
     * @description
     * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
     * stripped since angular uses this notation internally.
     *
     * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
     * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
     *    If set to an integer, the JSON output will contain that many spaces per indentation.
     * @returns {string|undefined} JSON-ified string representing `obj`.
     */
    vum.toJson = function (obj, pretty) {
      if (vum.isUndefined(obj)) return undefined
      if (!vum.isNumber(pretty)) {
        pretty = pretty ? 2 : null
      }
      return JSON.stringify(obj, vum.toJsonReplacer, pretty)
    }

    function isArrayLike (obj) {
      // `null`, `undefined` and `window` are not array-like
      if (obj == null || isWindow(obj)) return false

      // arrays, strings and jQuery/jqLite objects are array like
      // * jqLite is either the jQuery or jqLite constructor function
      // * we have to check the existence of jqLite first as this method is called
      //   via the forEach method when constructing the jqLite object in the first place
      if (isArray(obj) || vum.isString(obj) || (jqLite && obj instanceof jqLite)) return true

      // Support: iOS 8.2 (not reproducible in simulator)
      // "length" in obj used to prevent JIT error (gh-11508)
      var length = 'length' in Object(obj) && obj.length

      // NodeList objects (with `item` method) and
      // other objects with suitable length characteristics are array-like
      return vum.isNumber(length) &&
        (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function')
    }

    /**
     * @ngdoc function
     * @name angular.forEach
     * @module ng
     * @kind function
     *
     * @description
     * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
     * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
     * is the value of an object property or an array element, `key` is the object property key or
     * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
     *
     * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
     * using the `hasOwnProperty` method.
     *
     * Unlike ES262's
     * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
     * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
     * return the value provided.
     *
     ```js
     var values = {name: 'misko', gender: 'male'};
     var log = [];
     angular.forEach(values, function(value, key) {
       this.push(key + ': ' + value);
     }, log);
     expect(log).toEqual(['name: misko', 'gender: male']);
     ```
     *
     * @param {Object|Array} obj Object to iterate over.
     * @param {Function} iterator Iterator function.
     * @param {Object=} context Object to become context (`this`) for the iterator function.
     * @returns {Object|Array} Reference to `obj`.
     */

    vum.forEach = function (obj, iterator, context) {
      var key, length
      if (obj) {
        if (vum.isFunction(obj)) {
          for (key in obj) {
            // Need to check if hasOwnProperty exists,
            // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
            if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
              iterator.call(context, obj[key], key, obj)
            }
          }
        } else if (isArray(obj) || isArrayLike(obj)) {
          var isPrimitive = typeof obj !== 'object'
          for (key = 0, length = obj.length; key < length; key++) {
            if (isPrimitive || key in obj) {
              iterator.call(context, obj[key], key, obj)
            }
          }
        } else if (obj.forEach && obj.forEach !== forEach) {
          obj.forEach(iterator, context, obj)
        } else if (vum.isBlankObject(obj)) {
          // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
          for (key in obj) {
            iterator.call(context, obj[key], key, obj)
          }
        } else if (typeof obj.hasOwnProperty === 'function') {
          // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
          for (key in obj) {
            if (obj.hasOwnProperty(key)) {
              iterator.call(context, obj[key], key, obj)
            }
          }
        } else {
          // Slow path for objects which do not have a method `hasOwnProperty`
          for (key in obj) {
            if (hasOwnProperty.call(obj, key)) {
              iterator.call(context, obj[key], key, obj)
            }
          }
        }
      }
      return obj
    }

    vum.forEachSorted = function (obj, iterator, context) {
      var keys = Object.keys(obj).sort()
      for (var i = 0; i < keys.length; i++) {
        iterator.call(context, obj[keys[i]], keys[i])
      }
      return keys
    }
  })(window.vum)
})()
