github.com/sym3tri/etcd@v0.2.1-0.20140422215517-a563d82f95d6/mod/dashboard/app/coreos-web/coreos.js (about)

     1  'use strict';
     2  
     3  angular.module('underscore', []).factory('_', function($window) {
     4    return $window._;
     5  });
     6  
     7  angular.module('jquery', []).factory('$', function($window) {
     8    return $window.$;
     9  });
    10  
    11  angular.module('d3', []).factory('d3', function($window) {
    12    return $window.d3;
    13  });
    14  
    15  angular.module('coreos.services', [
    16    'coreos.events',
    17    'underscore',
    18    'jquery'
    19  ]);
    20  angular.module('coreos.ui', [
    21    'coreos.events',
    22    'underscore',
    23    'jquery',
    24    'd3',
    25    'ui.bootstrap'
    26  ]);
    27  angular.module('coreos.filters', []);
    28  angular.module('coreos.events', []);
    29  angular.module('coreos', [
    30    'coreos.events',
    31    'coreos.services',
    32    'coreos.ui',
    33    'coreos.filters',
    34    'coreos-templates-html',
    35    'coreos-templates-svg',
    36  
    37    // External deps.
    38    'ngRoute',
    39    'ngResource',
    40    'ngAnimate',
    41    'ui.bootstrap',
    42    'underscore',
    43    'jquery',
    44    'd3'
    45  ])
    46  .config(function($compileProvider) {
    47    // Allow irc links.
    48    $compileProvider
    49      .aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|irc):/);
    50  });
    51  
    52  'use strict';
    53  
    54  angular.module('coreos.filters')
    55  .filter('orderObjectBy', function() {
    56    return function(items, field, reverse) {
    57      var filtered = [];
    58      angular.forEach(items, function(item) {
    59        filtered.push(item);
    60      });
    61      filtered.sort(function (a, b) {
    62        return (a[field] > b[field]);
    63      });
    64      if (reverse) {
    65        filtered.reverse();
    66      }
    67      return filtered;
    68    };
    69  });
    70  
    71  'use strict';
    72  
    73  angular.module('coreos.filters')
    74  .filter('utc', function(_) {
    75  
    76    function convertToUtc(date) {
    77      return new Date(date.getUTCFullYear(),
    78          date.getUTCMonth(),
    79          date.getUTCDate(),
    80          date.getUTCHours(),
    81          date.getUTCMinutes(),
    82          date.getUTCSeconds());
    83    }
    84  
    85    return function(input) {
    86      if (_.isNumber(input)) {
    87        return convertToUtc(new Date(input));
    88      }
    89      if (_.isString(input)) {
    90        return convertToUtc(new Date(Date.parse(input)));
    91      }
    92      if (_.isDate(input)) {
    93        return convertToUtc(input);
    94      }
    95      return '';
    96    };
    97  
    98  });
    99  
   100  /**
   101   * Broadcast when the window size breakpoints change.
   102   * TODO(sym3tri): change implementation to use window.matchMedia instead.
   103   */
   104  
   105  'use strict';
   106  
   107  angular.module('coreos.services')
   108  .factory('breakpointSvc', function(_, $window, $rootScope, CORE_CONST,
   109        CORE_EVENT) {
   110  
   111    var previousName;
   112  
   113    function getSize() {
   114      var width = $window.innerWidth;
   115      return _.find(CORE_CONST.BREAKPOINTS, function(bp) {
   116        if (bp.min <= width && bp.max > width) {
   117          return true;
   118        }
   119      }).name;
   120    }
   121  
   122    function onResize() {
   123      var breakpointName = getSize();
   124      if (breakpointName !== previousName) {
   125        $rootScope.$broadcast(CORE_EVENT.BREAKPOINT, breakpointName);
   126        previousName = breakpointName;
   127      }
   128    }
   129  
   130    // Broadcast initial size.
   131    $rootScope.$broadcast(CORE_EVENT.BREAKPOINT, getSize());
   132  
   133    // Watch for resizes.
   134    angular.element($window).on('resize', _.debounce(onResize, 20, true));
   135  
   136    return {
   137      getSize: getSize
   138    };
   139  
   140  });
   141  
   142  'use strict';
   143  
   144  angular.module('coreos.services').provider('configSvc', function() {
   145    var configValues = {};
   146  
   147    this.config = function(newConfig) {
   148      if (newConfig) {
   149        configValues = newConfig;
   150      } else {
   151        return configValues;
   152      }
   153    };
   154  
   155    this.$get = function() {
   156      return {
   157        get: function(key) {
   158          if (key) {
   159            return configValues[key];
   160          } else {
   161            return angular.copy(configValues);
   162          }
   163        },
   164  
   165        set: function(key, value) {
   166          configValues[key] = value;
   167        }
   168      };
   169    };
   170  
   171  });
   172  
   173  'use strict';
   174  
   175  angular.module('coreos').constant('CORE_CONST', {
   176  
   177    HIGHLIGHT_CSS_CLASS: 'co-an-highlight',
   178  
   179    BREAKPOINTS: [
   180      {
   181        name: 'xs',
   182        min: 0,
   183        max: 480
   184      },
   185      {
   186        name: 'sm',
   187        min: 480,
   188        max: 768
   189      },
   190      {
   191        name: 'md',
   192        min: 768,
   193        max: 992
   194      },
   195      {
   196        name: 'lg',
   197        min: 992,
   198        max: 1200
   199      },
   200      {
   201        name: 'xl',
   202        min: 1200,
   203        max: Infinity
   204      }
   205    ]
   206  
   207  });
   208  
   209  /**
   210   * @fileoverview
   211   *
   212   * Service for working with cookies since angular's built-in cookie service
   213   * leaves much to be desired.
   214   */
   215  
   216  'use strict';
   217  
   218  angular.module('coreos.services').factory('cookieSvc',
   219      function($window, timeSvc) {
   220  
   221    return {
   222  
   223      /**
   224       * Create a new cookie.
   225       */
   226      create: function(name, value, daysUtilExpires) {
   227        var date, expires;
   228        if (daysUtilExpires) {
   229          date = new Date();
   230          date.setTime(date.getTime() +
   231              (daysUtilExpires * timeSvc.ONE_DAY_IN_MS));
   232          expires = '; expires=' + date.toGMTString();
   233        }
   234        else {
   235          expires = '';
   236        }
   237        $window.document.cookie = name + '=' + value + expires + '; path=/';
   238      },
   239  
   240      /**
   241       * Retrieve a cookie by name.
   242       */
   243      get: function(name) {
   244        var nameEq, cookieList, i, cookieStr;
   245        nameEq = name + '=';
   246        cookieList = $window.document.cookie.split(';');
   247        for (i = 0; i < cookieList.length; i++) {
   248          cookieStr = cookieList[i];
   249          while (cookieStr.charAt(0) === ' ') {
   250            cookieStr = cookieStr.substring(1, cookieStr.length);
   251          }
   252          if (cookieStr.indexOf(nameEq) === 0) {
   253            return cookieStr.substring(nameEq.length, cookieStr.length);
   254          }
   255        }
   256        return null;
   257      },
   258  
   259      /**
   260       * Delete a cookie by name.
   261       */
   262      remove: function(name) {
   263        this.create(name, '', -1);
   264      }
   265  
   266    };
   267  
   268  });
   269  
   270  /**
   271   * @fileoverview
   272   *
   273   * Simply inject this service to start broadcasting events.
   274   * It will feature-detect any available browser visibility api.
   275   * If the feature exists it will broadcast an event when browser visibiltiy
   276   * changes.
   277   */
   278  
   279  'use strict';
   280  
   281  angular.module('coreos.services')
   282  .factory('documentVisibilitySvc', function($rootScope, $document, _,
   283        CORE_EVENT) {
   284  
   285    var document = $document[0],
   286        features,
   287        detectedFeature;
   288  
   289    function broadcastChangeEvent() {
   290      $rootScope.$broadcast(CORE_EVENT.DOC_VISIBILITY_CHANGE,
   291          document[detectedFeature.propertyName]);
   292    }
   293  
   294    features = {
   295      standard: {
   296        eventName: 'visibilitychange',
   297        propertyName: 'hidden'
   298      },
   299      moz: {
   300        eventName: 'mozvisibilitychange',
   301        propertyName: 'mozHidden'
   302      },
   303      ms: {
   304        eventName: 'msvisibilitychange',
   305        propertyName: 'msHidden'
   306      },
   307      webkit: {
   308        eventName: 'webkitvisibilitychange',
   309        propertyName: 'webkitHidden'
   310      }
   311    };
   312  
   313    Object.keys(features).some(function(feature) {
   314      if (_.isBoolean(document[features[feature].propertyName])) {
   315        detectedFeature = features[feature];
   316        return true;
   317      }
   318    });
   319  
   320    if (detectedFeature) {
   321      $document.on(detectedFeature.eventName, broadcastChangeEvent);
   322    }
   323  
   324    return {
   325  
   326      /**
   327       * Is the window currently hidden or not.
   328       */
   329      isHidden: function() {
   330        if (detectedFeature) {
   331          return document[detectedFeature.propertyName];
   332        }
   333      }
   334  
   335    };
   336  
   337  });
   338  
   339  'use strict';
   340  
   341  angular.module('coreos.events').constant('CORE_EVENT', {
   342    PAGE_NOT_FOUND: 'core.event.page_not_found',
   343    BREAKPOINT: 'core.event.breakpoint',
   344    RESP_ERROR: 'core.event.resp_error',
   345    RESP_MUTATE: 'core.event.resp_mutate',
   346    DOC_VISIBILITY_CHANGE: 'core.event.doc_visibility_change',
   347    POLL_ERROR: 'core.event.poll_error'
   348  });
   349  
   350  /**
   351   * @fileoverview
   352   *
   353   * Utility service to highlight an element or selection of elements.
   354   * NOTE: Expects a [HIGHLIGHT_CSS_CLASS] class to be defined in constants.
   355   */
   356  
   357  'use strict';
   358  
   359  angular.module('coreos.services')
   360  .factory('highlighterSvc', function($timeout, $, CORE_CONST) {
   361  
   362    var pendingTimeout;
   363  
   364    return {
   365  
   366      /**
   367       * Highlight an element in the DOM.
   368       *
   369       * @param {String|Element} elemOrSelector
   370       */
   371      highlight: function(elemOrSelector) {
   372        var elem;
   373        if (!elemOrSelector) {
   374          return;
   375        }
   376        elem = $(elemOrSelector);
   377        if (elem.hasClass(CORE_CONST.HIGHLIGHT_CSS_CLASS)) {
   378          $timeout.cancel(pendingTimeout);
   379          elem.removeClass(CORE_CONST.HIGHLIGHT_CSS_CLASS);
   380        }
   381        elem.addClass(CORE_CONST.HIGHLIGHT_CSS_CLASS);
   382        pendingTimeout = $timeout(
   383            elem.removeClass.bind(elem, CORE_CONST.HIGHLIGHT_CSS_CLASS), 5000);
   384      }
   385  
   386    };
   387  
   388  });
   389  
   390  'use strict';
   391  
   392  angular.module('coreos.services')
   393  .factory('interceptorErrorSvc', function($q, $rootScope, CORE_EVENT) {
   394  
   395    function parseMessage(rejection) {
   396      var errorMsg;
   397      if (rejection.config.description) {
   398        errorMsg = 'Error attempting: ' + rejection.config.description;
   399      } else {
   400        errorMsg = 'A network error occurred.';
   401      }
   402      return errorMsg;
   403    }
   404  
   405    return {
   406  
   407      /**
   408       * For every failing $http request: broadcast an error event.
   409       */
   410      'responseError': function(rejection) {
   411        if (!rejection.config.supressNotifications) {
   412          $rootScope.$broadcast(CORE_EVENT.RESP_ERROR,
   413            rejection,
   414            parseMessage(rejection));
   415        }
   416        return $q.reject(rejection);
   417      }
   418  
   419    };
   420  
   421  });
   422  
   423  'use strict';
   424  
   425  angular.module('coreos.services')
   426  .factory('interceptorMutateSvc', function($q, $rootScope, CORE_EVENT) {
   427  
   428    // Remove last path segement of a url.
   429    function removeLastPath(url) {
   430      var newUrl = url.split('/');
   431      newUrl.pop();
   432      newUrl = newUrl.join('/');
   433      return newUrl;
   434    }
   435  
   436    return {
   437  
   438      /**
   439       * For every successful mutating $http request broadcast the urls.
   440       * Useful for cache invalidation.
   441       */
   442      'response': function(response) {
   443        var method = response.config.method,
   444            url = response.config.url,
   445            cacheKeys;
   446  
   447        if (method !== 'GET') {
   448          cacheKeys = [];
   449          cacheKeys.push(url);
   450          if (method !== 'POST') {
   451            cacheKeys.push(removeLastPath(url));
   452          }
   453          $rootScope.$broadcast(CORE_EVENT.RESP_MUTATE, response);
   454        }
   455        return response || $q.when(response);
   456      }
   457  
   458    };
   459  
   460  });
   461  
   462  /**
   463   * A general purpose polling service.
   464   *
   465   * Provide a series of options with callacks and this service will start a
   466   * poller for the task.
   467   *
   468   * On failure it will try up to `maxRetries`, then will be killed and callback
   469   * to the `catchMaxFail()` function if provided.
   470   *
   471   * Optionally pass in a `scope` associated with the poller to automatically
   472   * kill the poller when the scope is destroyed.
   473   *
   474   * Global settings for this provider can be configured in the app `config`
   475   * stage. Instance will override defaults if provided ot the `register()`
   476   * function.
   477   *
   478   * EXAMPLE USAGE:
   479   *
   480   *    poller.register('myPoller', {
   481   *      fn: functionToRunRepeadedly,
   482   *      then: successCallback,
   483   *      catch: errorCallback,
   484   *      catchMaxFail: afterMaxFailuresCallback,
   485   *      scope: $scope,
   486   *      startIn: 0,
   487   *      interval: 5000
   488   *    });
   489   */
   490  
   491  
   492  'use strict';
   493  
   494  angular.module('coreos.services').provider('pollerSvc', function() {
   495    var settings = {},
   496        pollers = {};
   497  
   498    /**
   499     * Update global settings for the provider.
   500     * @param {Object} newSettings
   501     */
   502    this.settings = function(newSettings) {
   503      if (newSettings) {
   504        settings = newSettings;
   505      } else {
   506        return settings;
   507      }
   508    };
   509  
   510    /**
   511     * The main factory method.
   512     * Dependencies are injected and is invoked by angular.
   513     */
   514    this.$get = function pollerFactory($q, $http, $timeout, _, CORE_EVENT) {
   515      /* jshint unused:false */
   516  
   517      function isRegistered(name) {
   518        return !!pollers[name];
   519      }
   520  
   521      /**
   522       * Schedule the `execute` function to run.
   523       * @param {Number} delay When to start in ms.
   524       */
   525      function schedule(name, executor, delay) {
   526        var poller = pollers[name];
   527        if (!poller || poller._errorCount > poller.maxRetries) {
   528          return;
   529        }
   530        poller._state = 'waiting';
   531        poller._timeoutPromise = $timeout(executor, delay);
   532      }
   533  
   534      /**
   535       * Wrap a function to prevent it from running if the current state
   536       * is "terminated".
   537       */
   538      function runIfActive(name, fn) {
   539        var poller = pollers[name];
   540        if (!poller) {
   541          return angular.noop;
   542        }
   543        return function() {
   544          if (poller._state !== 'terminated') {
   545            return fn.apply(null, arguments);
   546          }
   547        };
   548      }
   549  
   550      function killPoller(name) {
   551        var poller;
   552        if (!isRegistered(name)) {
   553          return;
   554        }
   555        poller = pollers[name];
   556        poller._state = 'terminated';
   557        // Cancel the interval timer.
   558        if (poller._timeoutPromise) {
   559          $timeout.cancel(poller._timeoutPromise);
   560        }
   561        // Remove the scope.$destroy handler.
   562        poller._unlistenDestroy();
   563        // Delete from the list.
   564        delete pollers[name];
   565      }
   566  
   567      /**
   568       * Create an executor function for a poller with the given name.
   569       */
   570      function createExecutor(name) {
   571        var poller = pollers[name];
   572        if (!poller) {
   573          return angular.noop;
   574        }
   575  
   576        /**
   577         * The main function that will be run on an interval for a poller.
   578         * This wraps the user-provided function, executes callbacks after
   579         * completion, and handles scheduling.
   580         */
   581        return function execute() {
   582          if (poller._paused) {
   583            schedule(name, poller._executor, poller.interval);
   584            return;
   585          }
   586          poller._state = 'executing';
   587          poller.fn()
   588            .then(runIfActive(name, function() {
   589              poller._state = 'success';
   590              poller._errorCount = 0;
   591              poller.then.apply(null, arguments);
   592            }))
   593            .catch(runIfActive(name, function() {
   594              var args;
   595              poller._state = 'error';
   596              poller._errorCount += 1;
   597              poller.catch.apply(null, arguments);
   598              if (poller._errorCount > poller.maxRetries) {
   599                args = _.toArray(arguments);
   600                args.unshift(name);
   601                poller.catchMaxFail.apply(null, args);
   602                killPoller(name);
   603              }
   604            }))
   605            .finally(runIfActive(name, function() {
   606              poller.finally.apply(null, arguments);
   607              schedule(name, poller._executor, poller.interval);
   608            }));
   609        };
   610      }
   611  
   612      return {
   613  
   614        /**
   615         * Determines if a poller is already registered by name.
   616         * @param {String} name
   617         * @return {Boolean}
   618         */
   619        isRegistered: isRegistered,
   620  
   621        /**
   622         * Register the promise in the index, and schedule it to start polling.
   623         *
   624         * @param {String} name The uniqe name to associate with the poller.
   625         * @param {Object} options
   626         */
   627        register: function(name, options) {
   628          // kill the old poller if one by same name already exists.
   629          if (isRegistered(name)) {
   630            this.kill(name);
   631          }
   632  
   633          // Initialize all poller options.
   634          _.defaults(options, settings, {
   635            startIn: 0,
   636            maxRetries: 0,
   637            catch: angular.noop,
   638            then: angular.noop,
   639            finally: angular.noop,
   640            catchMaxFail: function() {
   641              if (options.scope) {
   642                options.scope.$emit(CORE_EVENT.POLL_ERROR);
   643              }
   644            },
   645            _unlistenDestroy: angular.noop,
   646            _errorCount: 0,
   647            _state: 'starting'
   648          });
   649  
   650          if (options.scope) {
   651            // If a scope is provided, automatically kill the poller when the
   652            // scope is destroyed.
   653            options._unlistenDestroy =
   654              options.scope.$on('$destroy', this.kill.bind(this, name));
   655  
   656            // When scope is prvided automatically pause polling when tab
   657            // loses visability.
   658            // TODO: add pauseAll() function and move this to app.run()
   659            options.scope.$on(CORE_EVENT.DOC_VISIBILITY_CHANGE,
   660                function(e, isHidden) {
   661              if (isHidden) {
   662                options._paused = true;
   663              } else {
   664                options._paused = false;
   665              }
   666            });
   667          }
   668  
   669          // Keep track of the poller in the index.
   670          pollers[name] = options;
   671  
   672          // Generate the executor wrapper for the poller.
   673          options._executor = createExecutor(name);
   674  
   675          // Schedule the initial run of the poller.
   676          schedule(name, options._executor, options.startIn);
   677        },
   678  
   679        /**
   680         * Kill a poller by name and remove all references, callbacks, etc.
   681         * @param {String} name
   682         */
   683        kill: function(name) {
   684          killPoller(name);
   685        },
   686  
   687        /**
   688         * Kill all registered pollers.
   689         */
   690        killAll: function() {
   691          Object.keys(pollers).forEach(this.kill.bind(this));
   692        }
   693  
   694      };
   695  
   696    };
   697  
   698  });
   699  
   700  /**
   701   * @fileoverview
   702   *
   703   * Utility service that scrolls elements into view.
   704   */
   705  
   706  'use strict';
   707  
   708  angular.module('coreos.services')
   709  .factory('scrollerSvc', function($timeout, $) {
   710  
   711    function scroll(elem) {
   712      elem.first()[0].scrollIntoView();
   713    }
   714  
   715    var scrollerSvc = {
   716  
   717      /**
   718       * Scroll to the element on the page with matching id.
   719       * Adds and removes highlight classes too.
   720       *
   721       * @param {String|Element} elemOrSelector
   722       */
   723      scrollTo: function(elemOrSelector) {
   724        var maxTries = 100,
   725            numTries = 0,
   726            interval = 10,
   727            elem;
   728  
   729        if (!elemOrSelector) {
   730          return;
   731        }
   732  
   733        // Wait for element to appear in DOM if it doesn't exist yet,
   734        // then scroll to it.
   735        function attemptScroll() {
   736          elem = $(elemOrSelector);
   737          if (numTries < maxTries) {
   738            if (!elem.length) {
   739              numTries++;
   740              $timeout(attemptScroll, interval);
   741            } else {
   742              scroll(elem);
   743            }
   744          }
   745        }
   746  
   747        $timeout(attemptScroll, 0);
   748      }
   749  
   750    };
   751  
   752    return scrollerSvc;
   753  
   754  });
   755  
   756  'use strict';
   757  
   758  angular.module('coreos.services')
   759  .factory('arraySvc', function() {
   760  
   761    return {
   762  
   763      /**
   764       * Remove first occurance of an item from an array in-place.
   765       *
   766       * @param {Arrray} ary Array to mutate.
   767       * @param {*} item Array item to remove.
   768       * @return {Array} The input array.
   769       */
   770      remove: function(ary, item) {
   771        var index;
   772        if (!ary || !ary.length) {
   773          return [];
   774        }
   775        index = ary.indexOf(item);
   776        if (index > -1) {
   777          ary.splice(index, 1);
   778        }
   779        return ary;
   780      }
   781  
   782    };
   783  
   784  });
   785  
   786  'use strict';
   787  
   788  angular.module('coreos.services')
   789  .factory('mathSvc', function(_) {
   790  
   791    return {
   792  
   793      /**
   794       * If passed an array sums all items in the array.
   795       * Otherwise sums all arguments together.
   796       *
   797       * @param {Array|Number...}
   798       * @return {Number}
   799       */
   800      sum: function() {
   801        var ary;
   802        if (_.isArray(arguments[0])) {
   803          ary = arguments[0];
   804        } else {
   805          ary = _.toArray(arguments);
   806        }
   807        return ary.reduce(function(prev, curr) {
   808          return prev + curr;
   809        }, 0);
   810      }
   811  
   812    };
   813  
   814  });
   815  
   816  'use strict';
   817  
   818  angular.module('coreos.services')
   819  .factory('timeSvc', function(_) {
   820  
   821    var ONE_MINUTE_IN_MS = 60 * 1000,
   822        ONE_HOUR_IN_MS = ONE_MINUTE_IN_MS * 60,
   823        ONE_DAY_IN_MS = ONE_HOUR_IN_MS * 24,
   824        ONE_WEEK_IN_MS = ONE_DAY_IN_MS * 7,
   825        THIRTY_DAYS_IN_MS = ONE_DAY_IN_MS * 30;
   826  
   827    function getTimestamp(val) {
   828      if (val && _.isNumber(val)) {
   829        return val;
   830      }
   831      return Date.now();
   832    }
   833  
   834    return {
   835      ONE_MINUTE_IN_MS: ONE_MINUTE_IN_MS,
   836      ONE_HOUR_IN_MS: ONE_HOUR_IN_MS,
   837      ONE_DAY_IN_MS: ONE_DAY_IN_MS,
   838      ONE_WEEK_IN_MS: ONE_WEEK_IN_MS,
   839      THIRTY_DAYS_IN_MS: THIRTY_DAYS_IN_MS,
   840  
   841      milliToSecs: function(ms) {
   842        return Math.floor(ms / 1000);
   843      },
   844  
   845      secsToMins: function(secs) {
   846        return Math.floor(parseInt(secs, 10) / 60) || 0;
   847      },
   848  
   849      minsToSecs: function(mins) {
   850        return Math.abs(parseInt(mins, 10) * 60) || 0;
   851      },
   852  
   853      oneHourAgo: function(ts) {
   854        return getTimestamp(ts) - this.ONE_HOUR_IN_MS;
   855      },
   856  
   857      oneDayAgo: function(ts) {
   858        return getTimestamp(ts) - this.ONE_DAY_IN_MS;
   859      },
   860  
   861      oneWeekAgo: function(ts) {
   862        return getTimestamp(ts) - this.ONE_WEEK_IN_MS;
   863      },
   864  
   865      thirtyDaysAgo: function(ts) {
   866        return getTimestamp(ts) - this.THIRTY_DAYS_IN_MS;
   867      },
   868  
   869      getRelativeTimestamp: function(term) {
   870        var now = Date.now();
   871        switch(term) {
   872          case 'month':
   873            return this.thirtyDaysAgo(now);
   874          case 'week':
   875            return this.oneWeekAgo(now);
   876          case 'day':
   877            return this.oneDayAgo(now);
   878          case 'hour':
   879            return this.oneHourAgo(now);
   880        }
   881      }
   882  
   883    };
   884  
   885  });
   886  
   887  /**
   888   * @fileoverview
   889   * Wrap buttons and automatically enable/disbale and show loading indicator.
   890   */
   891  
   892  'use strict';
   893  
   894  angular.module('coreos.ui')
   895  .directive('coBtnBar', function($, $timeout, $compile) {
   896  
   897    return {
   898      templateUrl: '/coreos.ui/btn-bar/btn-bar.html',
   899      restrict: 'EA',
   900      transclude: true,
   901      replace: true,
   902      scope: {
   903        // A promise that indicates completion of async operation.
   904        'completePromise': '='
   905      },
   906      link: function(scope, elem) {
   907        var linkButton,
   908            loaderDirectiveEl;
   909  
   910        linkButton = $('.btn-link', elem).last();
   911        loaderDirectiveEl =
   912            angular.element('<co-inline-loader></co-inline-loader>');
   913        $compile(loaderDirectiveEl)(scope);
   914  
   915        function disableButtons() {
   916          elem.append(loaderDirectiveEl);
   917          $('button', elem).attr('disabled', 'disabled');
   918          linkButton.addClass('hidden');
   919        }
   920  
   921        function enableButtons() {
   922          loaderDirectiveEl.remove();
   923          $('button', elem).removeAttr('disabled');
   924          linkButton.removeClass('hidden');
   925        }
   926  
   927        scope.$watch('completePromise', function(completePromise) {
   928          if (completePromise) {
   929            // Force async execution so disabling the button won't prevent form
   930            // submission.
   931            $timeout(disableButtons, 0);
   932            completePromise.finally(function() {
   933              // Also enable buttons asynchronously in case the request completes
   934              // before disableButtons() runs.
   935              $timeout(enableButtons, 0);
   936            });
   937          }
   938        });
   939      }
   940  
   941    };
   942  
   943  });
   944  
   945  /**
   946   * Simple directive to navigate to a route when the
   947   * element is clicked on.
   948   */
   949  
   950  'use strict';
   951  
   952  angular.module('coreos.ui')
   953  .directive('coClickNav', function($location) {
   954  
   955    return {
   956      restrict: 'A',
   957      link: function(scope, elem, attrs) {
   958        function onClickHandler(event) {
   959          $location.url(attrs.coClickNav);
   960          scope.$apply();
   961          event.preventDefault();
   962          event.stopPropagation();
   963        }
   964        elem.on('click', onClickHandler);
   965        elem.on('$destroy', function() {
   966          elem.off('click', onClickHandler);
   967        });
   968      }
   969    };
   970  
   971  });
   972  
   973  /**
   974   * @fileoverview
   975   * Display a cog icon and construct dropdown menu.
   976   */
   977  
   978  'use strict';
   979  
   980  angular.module('coreos.ui')
   981  .directive('coCog', function() {
   982  
   983    return {
   984      templateUrl: '/coreos.ui/cog/cog.html',
   985      restrict: 'E',
   986      replace: true,
   987      scope: {
   988        'apps': '=',
   989        'options': '=',
   990        'size': '@',
   991        'anchor': '@'
   992      },
   993      link: function(scope, elem) {
   994        scope.clickHandler = function($event, option) {
   995          $event.stopPropagation();
   996          $event.preventDefault();
   997          if (option.callback) {
   998            option.callback();
   999          }
  1000          elem.removeClass('open');
  1001        };
  1002      }
  1003    };
  1004  
  1005  });
  1006  
  1007  'use strict';
  1008  
  1009  angular.module('coreos.ui')
  1010  .controller('ConfirmModalCtrl', function($scope, $modalInstance,
  1011        executeFn, title, message, btnText, errorFormatter) {
  1012  
  1013    $scope.errorFormatter = errorFormatter;
  1014    $scope.title = title;
  1015    $scope.message = message;
  1016    $scope.btnText = btnText || 'Confirm';
  1017  
  1018    $scope.execute = function() {
  1019      $scope.requestPromise = executeFn(null, {
  1020        supressNotifications: true
  1021      })
  1022      .then($modalInstance.close);
  1023    };
  1024  
  1025    $scope.cancel = function() {
  1026      $modalInstance.dismiss('cancel');
  1027    };
  1028  
  1029  });
  1030  
  1031  /**
  1032   * @fileoverview
  1033   * An arc donut chart.
  1034   */
  1035  
  1036  // TDOO(sym3tri): add hover text.
  1037  
  1038  'use strict';
  1039  
  1040  angular.module('coreos.ui')
  1041  .directive('coDonut', function(d3, _) {
  1042  
  1043    return {
  1044  
  1045      templateUrl: '/coreos.ui/donut/donut.html',
  1046      transclude: true,
  1047      restrict: 'E',
  1048      replace: true,
  1049      scope: {
  1050        // The original source data to graph.
  1051        percent: '=',
  1052        color: '@'
  1053      },
  1054      controller: function($scope) {
  1055        var outerRadius, circleWidth;
  1056        $scope.width = $scope.height = 80;
  1057        outerRadius = $scope.width / 2;
  1058        circleWidth = 15;
  1059        $scope.arc = d3.svg.arc()
  1060          .innerRadius(outerRadius - circleWidth)
  1061          .outerRadius(outerRadius)
  1062          .startAngle(0);
  1063        // Constant to turn percents into radian angles.
  1064        $scope.tau = 2 * Math.PI;
  1065      },
  1066      link: function(scope, elem) {
  1067        scope.isRendered = false;
  1068  
  1069        function render() {
  1070          var endAngle = scope.tau, // 100%
  1071              textColor = '#333',
  1072              bgcolor = '#eee',
  1073              color = scope.color || '#000',
  1074              fontSize = 18;
  1075  
  1076          // Keep track of added DOM elements.
  1077          scope.el = {};
  1078  
  1079          scope.el.svg = d3.select(elem.find('.co-m-gauge__content')[0])
  1080            .append('svg')
  1081            .attr('width', scope.width)
  1082            .attr('height', scope.height)
  1083            .append('g')
  1084              .attr('transform',
  1085                  'translate(' +
  1086                  scope.width / 2 + ',' +
  1087                  scope.height / 2 + ')');
  1088  
  1089          scope.el.text = scope.el.svg.append('text')
  1090            .attr('fill', textColor)
  1091            .attr('y', Math.floor(fontSize / 3))
  1092            .attr('font-size', fontSize + 'px')
  1093            .attr('text-anchor', 'middle');
  1094  
  1095          scope.el.arcGroup = scope.el.svg.append('g')
  1096            .attr('transform', 'rotate(180)');
  1097  
  1098          scope.el.background = scope.el.arcGroup.append('path')
  1099            .datum({
  1100              endAngle: endAngle
  1101            })
  1102            .style('fill', bgcolor)
  1103            .attr('d', scope.arc);
  1104  
  1105          scope.el.foreground = scope.el.arcGroup.append('path')
  1106            .datum({
  1107              endAngle: scope.tau * (scope.percent || 0)
  1108            })
  1109            .style('fill', color)
  1110            .style('opacity', 0.8)
  1111            .attr('d', scope.arc);
  1112  
  1113          scope.isRendered = true;
  1114        }
  1115  
  1116        /**
  1117         * Update the value of the donut chart.
  1118         */
  1119        function updateValue() {
  1120          if (!_.isNumber(scope.percent)) {
  1121            scope.el.text.text('?');
  1122            return;
  1123          }
  1124          scope.el.text.text(Math.round(scope.percent * 100) + '%');
  1125          scope.el.foreground.transition()
  1126            .duration(750)
  1127            .call(arcTween, scope.percent * scope.tau);
  1128        }
  1129  
  1130        /**
  1131         * Transition function to animate the arc.
  1132         */
  1133        function arcTween(transition, newAngle) {
  1134          transition.attrTween('d', function(d) {
  1135            var interpolate = d3.interpolate(d.endAngle, newAngle);
  1136            return function(t) {
  1137              d.endAngle = interpolate(t);
  1138              return scope.arc(d);
  1139            };
  1140          });
  1141        }
  1142  
  1143        /**
  1144         * Cleanup.
  1145         */
  1146        elem.on('$destroy', function() {
  1147          scope.el.svg.remove();
  1148        });
  1149  
  1150        render();
  1151  
  1152        scope.$watch('percent', function() {
  1153          if (scope.isRendered) {
  1154            updateValue();
  1155          }
  1156        });
  1157      }
  1158    };
  1159  
  1160  });
  1161  
  1162  /**
  1163   * @fileoverview
  1164   * Displays a message based on a promise.
  1165   */
  1166  
  1167  'use strict';
  1168  angular.module('coreos.ui')
  1169  
  1170  
  1171  .provider('errorMessageSvc', function() {
  1172  
  1173    var formatters = {};
  1174  
  1175    this.registerFormatter = function(name, fn) {
  1176      formatters[name] = fn;
  1177    };
  1178  
  1179    this.$get = function() {
  1180      return {
  1181        getFormatter: function(name) {
  1182          return formatters[name] || angular.noop;
  1183        }
  1184      };
  1185    };
  1186  
  1187  })
  1188  
  1189  
  1190  .directive('coErrorMessage', function(errorMessageSvc) {
  1191  
  1192    return {
  1193      templateUrl: '/coreos.ui/error-message/error-message.html',
  1194      restrict: 'E',
  1195      replace: true,
  1196      scope: {
  1197        promise: '=',
  1198        formatter: '@',
  1199        customMessage: '@message'
  1200      },
  1201      controller: function postLink($scope) {
  1202        $scope.show = false;
  1203  
  1204        function handler(resp) {
  1205          if ($scope.formatter) {
  1206            $scope.message =
  1207              errorMessageSvc.getFormatter($scope.formatter)(resp);
  1208          } else if ($scope.customMessage) {
  1209            $scope.message = $scope.customMessage;
  1210          } else {
  1211            return;
  1212          }
  1213          $scope.show = true;
  1214        }
  1215  
  1216        $scope.$watch('promise', function(promise) {
  1217          $scope.show = false;
  1218          if (promise && promise.catch) {
  1219            promise.catch(handler);
  1220          }
  1221        });
  1222  
  1223      }
  1224    };
  1225  
  1226  });
  1227  
  1228  /**
  1229   * @fileoverview
  1230   * Inject favicons into the <head>.
  1231   * Only use on <head> tag.
  1232   */
  1233  
  1234  
  1235  'use strict';
  1236  angular.module('coreos.ui')
  1237  
  1238  .directive('coFavicons', function($compile, $rootScope, configSvc) {
  1239    /*jshint maxlen:false */
  1240  
  1241    return {
  1242      restrict: 'A',
  1243      replace: true,
  1244      link: function postLink(scope, elem) {
  1245        var newScope = $rootScope.$new(),
  1246        htmlTemplate =
  1247          '<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{path}}/apple-touch-icon-144-precomposed.png">' +
  1248          '<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{{path}}/apple-touch-icon-114-precomposed.png">' +
  1249          '<link rel="apple-touch-icon-precomposed" sizes="72x72" href="{{path}}/apple-touch-icon-72-precomposed.png">' +
  1250          '<link rel="apple-touch-icon-precomposed" href="{{path}}/apple-touch-icon-57-precomposed.png">' +
  1251          '<link rel="shortcut icon" href="{{path}}/favicon.png">';
  1252        newScope.path = configSvc.get('libPath') + '/img';
  1253        elem.append($compile(htmlTemplate)(newScope));
  1254      }
  1255    };
  1256  
  1257  });
  1258  
  1259  /*
  1260  */
  1261  
  1262  /**
  1263   * @fileoverview
  1264   * Standard CoreOS footer.
  1265   *
  1266   */
  1267  
  1268  'use strict';
  1269  angular.module('coreos.ui')
  1270  
  1271  .directive('coFooter', function() {
  1272    return {
  1273      templateUrl: '/coreos.ui/footer/footer.html',
  1274      transclude: true,
  1275      restrict: 'E',
  1276      replace: true
  1277    };
  1278  })
  1279  
  1280  .directive('coFooterLink', function() {
  1281    return {
  1282      templateUrl: '/coreos.ui/footer/footer-link.html',
  1283      transclude: true,
  1284      restrict: 'E',
  1285      replace: true,
  1286      scope: {
  1287        href: '@',
  1288        iconClass: '@'
  1289      }
  1290    };
  1291  })
  1292  
  1293  
  1294  /**
  1295   * Convenience wrapper for doing sticky footers.
  1296   */
  1297  .directive('coFooterWrapper', function() {
  1298    return {
  1299      templateUrl: '/coreos.ui/footer/footer-wrapper.html',
  1300      transclude: true,
  1301      restrict: 'E',
  1302      replace: true
  1303    };
  1304  
  1305  });
  1306  
  1307  /**
  1308   * @fileoverview
  1309   * Highlight an item when its bound data changes.
  1310   */
  1311  
  1312  'use strict';
  1313  
  1314  angular.module('coreos.ui')
  1315  .directive('coHighlight', function(highlighterSvc) {
  1316  
  1317    return {
  1318      restrict: 'A',
  1319      link: function(scope, elem, attrs) {
  1320  
  1321        scope.$watch(attrs.coHighlight, function(newValue, oldValue) {
  1322          if (newValue !== oldValue) {
  1323            highlighterSvc.highlight(elem);
  1324          }
  1325        });
  1326  
  1327      }
  1328    };
  1329  
  1330  });
  1331  
  1332  /**
  1333   * @fileoverview
  1334   *
  1335   * Inline loading indicator widget.
  1336   */
  1337  
  1338  'use strict';
  1339  angular.module('coreos.ui')
  1340  
  1341  .directive('coInlineLoader', function() {
  1342  
  1343    return {
  1344      templateUrl: '/coreos.ui/inline-loader/inline-loader.html',
  1345      restrict: 'E',
  1346      replace: true
  1347    };
  1348  
  1349  });
  1350  
  1351  /**
  1352   * @fileoverview
  1353   *
  1354   * Loading indicator that centers itself inside its parent.
  1355   */
  1356  
  1357  
  1358  'use strict';
  1359  angular.module('coreos.ui')
  1360  
  1361  .directive('coLoader', function() {
  1362    return {
  1363      templateUrl: '/coreos.ui/loader/loader.html',
  1364      restrict: 'E',
  1365      replace: true
  1366    };
  1367  });
  1368  
  1369  /**
  1370   * @fileoverview
  1371   * Display page title with primary action link.
  1372   */
  1373  
  1374  
  1375  'use strict';
  1376  angular.module('coreos.ui')
  1377  
  1378  .directive('coNavTitle', function() {
  1379    return {
  1380      templateUrl: '/coreos.ui/nav-title/nav-title.html',
  1381      transclude: true,
  1382      restrict: 'E',
  1383      replace: true,
  1384      scope: {
  1385        title: '@'
  1386      }
  1387    };
  1388  });
  1389  
  1390  /**
  1391   * @fileoverview
  1392   * Top navbar which inlcudes nav links.
  1393   */
  1394  
  1395  
  1396  'use strict';
  1397  angular.module('coreos.ui')
  1398  
  1399  .directive('coNavbar', function(configSvc) {
  1400  
  1401    return {
  1402      templateUrl: '/coreos.ui/navbar/navbar.html',
  1403      transclude: true,
  1404      restrict: 'E',
  1405      replace: true,
  1406      controller: function($scope) {
  1407        $scope.config = configSvc.get();
  1408        $scope.isCollapsed = true;
  1409      }
  1410    };
  1411  
  1412  })
  1413  
  1414  
  1415  /**
  1416   * Simple directive to create bootstrap friendly navbar links.
  1417   * Will automatically add the 'active' class based on the route.
  1418   */
  1419  .directive('coNavbarLink', function($location) {
  1420  
  1421    return {
  1422      templateUrl: '/coreos.ui/navbar/navbar-link.html',
  1423      transclude: true,
  1424      restrict: 'E',
  1425      replace: true,
  1426      scope: {
  1427        // The path to link to.
  1428        'href': '@'
  1429      },
  1430      link: function(scope) {
  1431        scope.isActive = function() {
  1432          return $location.path() === scope.href;
  1433        };
  1434      }
  1435    };
  1436  
  1437  })
  1438  
  1439  /**
  1440   * Optional dropdown menu to put in the right of the navbar.
  1441   */
  1442  .directive('coNavbarDropdown', function() {
  1443  
  1444    return {
  1445      templateUrl: '/coreos.ui/navbar/navbar-dropdown.html',
  1446      transclude: true,
  1447      restrict: 'E',
  1448      replace: true,
  1449      scope: {
  1450        text: '@'
  1451      }
  1452    };
  1453  
  1454  });
  1455  
  1456  
  1457  /**
  1458   * @fileoverview
  1459   * Directive to easily inline svg images.
  1460   * NOTE: kind of a hack to get ng-include to work properly within a directive
  1461   * without wrapping it with an extra DOM element.
  1462   */
  1463  
  1464  'use strict';
  1465  
  1466  angular.module('coreos.ui')
  1467  .directive('coSvg', function($, $rootScope, $compile) {
  1468  
  1469    return {
  1470      template: '<div></div>',
  1471      restrict: 'E',
  1472      replace: true,
  1473      scope: {
  1474        src: '@',
  1475        width: '@',
  1476        height: '@'
  1477      },
  1478      link: function(scope, elem, attrs) {
  1479        var containerEl, html, newScope;
  1480        newScope = $rootScope.$new();
  1481        html = '<div class="co-m-svg" '+
  1482                'ng-class="classes" ng-style="style" ng-include="src"></div>';
  1483        newScope.style = {};
  1484        if (scope.width) {
  1485          newScope.style.width = scope.width + 'px';
  1486        }
  1487        if (scope.height) {
  1488          newScope.style.height = scope.height + 'px';
  1489        }
  1490        if (attrs.class) {
  1491          newScope.classes = attrs.class;
  1492        }
  1493        scope.$watch('src', function(src) {
  1494          if (src) {
  1495            newScope.src = src;
  1496            containerEl = $compile(html)(newScope);
  1497            elem.replaceWith(containerEl);
  1498          }
  1499        });
  1500      }
  1501    };
  1502  
  1503  });
  1504  
  1505  'use strict';
  1506  
  1507  angular.module('coreos.ui')
  1508  .directive('coTextCopy', function() {
  1509  
  1510    return {
  1511      restrict: 'A',
  1512      replace: true,
  1513      link: function(scope, elem) {
  1514        function onClickHandler(event) {
  1515          elem.select();
  1516          event.preventDefault();
  1517          event.stopPropagation();
  1518        }
  1519        elem.on('click', onClickHandler);
  1520        elem.on('$destroy', function() {
  1521          elem.off('click', onClickHandler);
  1522        });
  1523      }
  1524    };
  1525  
  1526  });
  1527  
  1528  /**
  1529   * @fileoverview
  1530   *
  1531   * Keeps the title tag updated.
  1532   */
  1533  
  1534  'use strict';
  1535  angular.module('coreos.ui')
  1536  
  1537  
  1538  .directive('coTitle', function() {
  1539  
  1540    return {
  1541      transclude: false,
  1542      restrict: 'A',
  1543      scope: {
  1544        suffix: '@coTitleSuffix'
  1545      },
  1546      controller: function($scope, $rootScope, $route) {
  1547        $scope.pageTitle = '';
  1548        $scope.defaultTitle = null;
  1549        $rootScope.$on('$routeChangeSuccess', function() {
  1550          $scope.pageTitle = $route.current.title || $route.current.$$route.title;
  1551        });
  1552      },
  1553      link: function(scope, elem) {
  1554        scope.$watch('pageTitle', function(title) {
  1555          if (title) {
  1556            if (!scope.defaultTitle) {
  1557              scope.defaultTitle = elem.text();
  1558            }
  1559            elem.text(title + ' ' + scope.suffix);
  1560          } else {
  1561            if (scope.defaultTitle) {
  1562              elem.text(scope.defaultTitle);
  1563            }
  1564          }
  1565        });
  1566      }
  1567    };
  1568  
  1569  });
  1570  
  1571  /**
  1572   * @fileoverview
  1573   * Directive to display global error or info messages.
  1574   * Enqueue messages through the toastSvc.
  1575   */
  1576  
  1577  
  1578  'use strict';
  1579  
  1580  angular.module('coreos.ui')
  1581  .directive('coToast', function() {
  1582    return {
  1583      templateUrl: '/coreos.ui/toast/toast.html',
  1584      restrict: 'E',
  1585      replace: true,
  1586      scope: true,
  1587      controller: function($scope, toastSvc) {
  1588        $scope.messages = toastSvc.messages;
  1589        $scope.dismiss = toastSvc.dismiss;
  1590      }
  1591    };
  1592  });
  1593  
  1594  
  1595  angular.module('coreos.services')
  1596  .factory('toastSvc', function($timeout) {
  1597  
  1598    var AUTO_DISMISS_TIME = 5000,
  1599        service,
  1600        lastTimeoutPromise;
  1601  
  1602    function dequeue() {
  1603      if (service.messages.length) {
  1604        service.messages.shift();
  1605      }
  1606    }
  1607  
  1608    function enqueue(type, text) {
  1609      service.messages.push({
  1610        type: type,
  1611        text: text
  1612      });
  1613      lastTimeoutPromise = $timeout(dequeue, AUTO_DISMISS_TIME);
  1614    }
  1615  
  1616    function cancelTimeout() {
  1617      if (lastTimeoutPromise) {
  1618        $timeout.cancel(lastTimeoutPromise);
  1619      }
  1620    }
  1621  
  1622    service = {
  1623  
  1624      messages: [],
  1625  
  1626      error: enqueue.bind(null, 'error'),
  1627  
  1628      info: enqueue.bind(null, 'info'),
  1629  
  1630      dismiss: function(index) {
  1631        cancelTimeout();
  1632        service.messages.splice(index, 1);
  1633      },
  1634  
  1635      dismissAll: function() {
  1636        cancelTimeout();
  1637        service.messages.length = 0;
  1638      }
  1639  
  1640    };
  1641  
  1642    return service;
  1643  
  1644  });
  1645  
  1646  angular.module('coreos-templates-html', ['/coreos.ui/btn-bar/btn-bar.html', '/coreos.ui/cog/cog.html', '/coreos.ui/confirm-modal/confirm-modal.html', '/coreos.ui/donut/donut.html', '/coreos.ui/error-message/error-message.html', '/coreos.ui/favicons/favicons.html', '/coreos.ui/footer/footer-link.html', '/coreos.ui/footer/footer-wrapper.html', '/coreos.ui/footer/footer.html', '/coreos.ui/inline-loader/inline-loader.html', '/coreos.ui/loader/loader.html', '/coreos.ui/nav-title/nav-title.html', '/coreos.ui/navbar/navbar-dropdown.html', '/coreos.ui/navbar/navbar-link.html', '/coreos.ui/navbar/navbar.html', '/coreos.ui/toast/toast.html']);
  1647  
  1648  angular.module("/coreos.ui/btn-bar/btn-bar.html", []).run(["$templateCache", function($templateCache) {
  1649    $templateCache.put("/coreos.ui/btn-bar/btn-bar.html",
  1650      "<div class=\"co-m-btn-bar\" ng-transclude>\n" +
  1651      "</div>\n" +
  1652      "");
  1653  }]);
  1654  
  1655  angular.module("/coreos.ui/cog/cog.html", []).run(["$templateCache", function($templateCache) {
  1656    $templateCache.put("/coreos.ui/cog/cog.html",
  1657      "<div class=\"co-m-cog\">\n" +
  1658      "  <span class=\"co-m-cog__icon co-m-cog__icon--size-{{size}} dropdown-toggle fa fa-cog\"></span>\n" +
  1659      "  <ul class=\"dropdown-menu co-m-cog__dropdown co-m-dropdown--dark co-m-cog__dropdown--anchor-{{anchor}}\">\n" +
  1660      "    <li ng-repeat=\"option in options | orderBy:'weight'\">\n" +
  1661      "      <a ng-if=\"option.href\" ng-href=\"{{option.href}}\">{{option.label}}</a>\n" +
  1662      "      <a ng-if=\"!option.href\" ng-click=\"clickHandler($event, option)\">{{option.label}}</a>\n" +
  1663      "    </li>\n" +
  1664      "  </ul>\n" +
  1665      "</div>\n" +
  1666      "");
  1667  }]);
  1668  
  1669  angular.module("/coreos.ui/confirm-modal/confirm-modal.html", []).run(["$templateCache", function($templateCache) {
  1670    $templateCache.put("/coreos.ui/confirm-modal/confirm-modal.html",
  1671      "<div>\n" +
  1672      "  <form ng-submit=\"execute()\" name=\"form\" role=\"form\">\n" +
  1673      "    <div class=\"modal-header\">\n" +
  1674      "      <h4 class=\"modal-title\" ng-bind=\"title\"></h4>\n" +
  1675      "    </div>\n" +
  1676      "    <div class=\"modal-body\" ng-bind=\"message\"></div>\n" +
  1677      "    <div class=\"modal-footer\" co-btn-bar complete-promise=\"requestPromise\">\n" +
  1678      "      <co-error-message formatter=\"{{errorFormatter}}\" promise=\"requestPromise\"></co-error-message>\n" +
  1679      "      <button type=\"submit\" class=\"btn btn-primary\" ng-bind=\"btnText\"></button>\n" +
  1680      "      <button type=\"button\" ng-click=\"cancel()\" class=\"btn btn-link\">Cancel</button>\n" +
  1681      "    </div>\n" +
  1682      "  </form>\n" +
  1683      "</div>\n" +
  1684      "");
  1685  }]);
  1686  
  1687  angular.module("/coreos.ui/donut/donut.html", []).run(["$templateCache", function($templateCache) {
  1688    $templateCache.put("/coreos.ui/donut/donut.html",
  1689      "<div class=\"co-m-donut co-m-gauge\">\n" +
  1690      "  <div class=\"co-m-gauge__content\"></div>\n" +
  1691      "  <div class=\"co-m-gauge__label\" ng-transclude></div>\n" +
  1692      "</div>\n" +
  1693      "");
  1694  }]);
  1695  
  1696  angular.module("/coreos.ui/error-message/error-message.html", []).run(["$templateCache", function($templateCache) {
  1697    $templateCache.put("/coreos.ui/error-message/error-message.html",
  1698      "<div ng-show=\"show\" class=\"co-m-message co-m-message--error co-an-fade-in-out ng-hide\">{{message}}</div>\n" +
  1699      "");
  1700  }]);
  1701  
  1702  angular.module("/coreos.ui/favicons/favicons.html", []).run(["$templateCache", function($templateCache) {
  1703    $templateCache.put("/coreos.ui/favicons/favicons.html",
  1704      "");
  1705  }]);
  1706  
  1707  angular.module("/coreos.ui/footer/footer-link.html", []).run(["$templateCache", function($templateCache) {
  1708    $templateCache.put("/coreos.ui/footer/footer-link.html",
  1709      "<a class=\"co-m-footer-link\" href=\"{{href}}\">\n" +
  1710      "  <span class=\"co-m-footer-link--icon\" ng-if=\"iconClass\" ng-class=\"iconClass\"></span>\n" +
  1711      "  <span ng-transclude></span>\n" +
  1712      "</a>\n" +
  1713      "");
  1714  }]);
  1715  
  1716  angular.module("/coreos.ui/footer/footer-wrapper.html", []).run(["$templateCache", function($templateCache) {
  1717    $templateCache.put("/coreos.ui/footer/footer-wrapper.html",
  1718      "<div id=\"co-l-footer-wrapper\">\n" +
  1719      "  <div ng-transclude></div>\n" +
  1720      "  <div id=\"co-l-footer-push\"></div>\n" +
  1721      "</div>\n" +
  1722      "");
  1723  }]);
  1724  
  1725  angular.module("/coreos.ui/footer/footer.html", []).run(["$templateCache", function($templateCache) {
  1726    $templateCache.put("/coreos.ui/footer/footer.html",
  1727      "<div id=\"co-l-footer\">\n" +
  1728      "  <div class=\"container\" ng-transclude></div>\n" +
  1729      "</div>\n" +
  1730      "");
  1731  }]);
  1732  
  1733  angular.module("/coreos.ui/inline-loader/inline-loader.html", []).run(["$templateCache", function($templateCache) {
  1734    $templateCache.put("/coreos.ui/inline-loader/inline-loader.html",
  1735      "<div class=\"co-m-inline-loader co-an-fade-in-out\">\n" +
  1736      "  <div class=\"co-m-inline-loader-dot__one\"></div>\n" +
  1737      "  <div class=\"co-m-inline-loader-dot__two\"></div>\n" +
  1738      "  <div class=\"co-m-inline-loader-dot__three\"></div>\n" +
  1739      "</div>\n" +
  1740      "");
  1741  }]);
  1742  
  1743  angular.module("/coreos.ui/loader/loader.html", []).run(["$templateCache", function($templateCache) {
  1744    $templateCache.put("/coreos.ui/loader/loader.html",
  1745      "<div class=\"co-m-loader co-an-fade-in-out\">\n" +
  1746      "  <span class=\"co-m-loader__spinner\"></span>\n" +
  1747      "</div>\n" +
  1748      "");
  1749  }]);
  1750  
  1751  angular.module("/coreos.ui/nav-title/nav-title.html", []).run(["$templateCache", function($templateCache) {
  1752    $templateCache.put("/coreos.ui/nav-title/nav-title.html",
  1753      "<div class=\"co-m-nav-title row\">\n" +
  1754      "  <div ng-transclude class=\"col-lg-3 col-md-3 col-sm-3 col-xs-6\"></div>\n" +
  1755      "  <div class=\"col-lg-6 col-md-6 col-sm-6 col-xs-12\">\n" +
  1756      "    <h1 class=\"co-m-page-title co-fx-text-shadow\">{{title}}</h1>\n" +
  1757      "  </div>\n" +
  1758      "</div>\n" +
  1759      "");
  1760  }]);
  1761  
  1762  angular.module("/coreos.ui/navbar/navbar-dropdown.html", []).run(["$templateCache", function($templateCache) {
  1763    $templateCache.put("/coreos.ui/navbar/navbar-dropdown.html",
  1764      "<ul class=\"nav navbar-nav pull-right\">\n" +
  1765      "  <li class=\"dropdown pull-right\">\n" +
  1766      "    <a href=\"#\" class=\"dropdown-toggle\">{{text}} <b class=\"caret\"></b></a>\n" +
  1767      "    <ul ng-transclude class=\"dropdown-menu co-m-dropdown--dark\"></ul>\n" +
  1768      "  </li>\n" +
  1769      "</ul>\n" +
  1770      "");
  1771  }]);
  1772  
  1773  angular.module("/coreos.ui/navbar/navbar-link.html", []).run(["$templateCache", function($templateCache) {
  1774    $templateCache.put("/coreos.ui/navbar/navbar-link.html",
  1775      "<li class=\"co-m-nav-link\" ng-class=\"{'active': isActive()}\">\n" +
  1776      "  <a ng-href=\"{{href}}\" ng-transclude></a>\n" +
  1777      "</li>\n" +
  1778      "");
  1779  }]);
  1780  
  1781  angular.module("/coreos.ui/navbar/navbar.html", []).run(["$templateCache", function($templateCache) {
  1782    $templateCache.put("/coreos.ui/navbar/navbar.html",
  1783      "<div class=\"co-m-navbar co-fx-box-shadow navbar navbar-fixed-top\">\n" +
  1784      "\n" +
  1785      "  <div class=\"navbar-header\">\n" +
  1786      "    <button ng-click=\"isCollapsed = !isCollapsed\" class=\"navbar-toggle\" type=\"button\">\n" +
  1787      "      <span class=\"glyphicon glyphicon-align-justify\"></span>\n" +
  1788      "    </button>\n" +
  1789      "    <a ng-href=\"{{config.siteBasePath}}\" class=\"navbar-brand\">\n" +
  1790      "      <co-svg class=\"co-m-navbar__logo\" src=\"/coreos.svg/logo.svg\"></co-svg>\n" +
  1791      "    </a>\n" +
  1792      "  </div>\n" +
  1793      "\n" +
  1794      "  <div collapse=\"isCollapsed\" ng-transclude class=\"collapse navbar-collapse\"></div>\n" +
  1795      "\n" +
  1796      "</div>\n" +
  1797      "");
  1798  }]);
  1799  
  1800  angular.module("/coreos.ui/toast/toast.html", []).run(["$templateCache", function($templateCache) {
  1801    $templateCache.put("/coreos.ui/toast/toast.html",
  1802      "<div class=\"co-m-toast\">\n" +
  1803      "  <div ng-repeat=\"message in messages\"\n" +
  1804      "      class=\"co-m-toast__message co-m-message co-m-message--{{message.type}} co-an-fade-in-out co-fx-box-shadow\">\n" +
  1805      "    {{message.text}}\n" +
  1806      "    <span ng-click=\"dismiss($index)\" class=\"pull-right glyphicon glyphicon-remove text-right co-m-message__close\"></span>\n" +
  1807      "  </div>\n" +
  1808      "</div>\n" +
  1809      "");
  1810  }]);
  1811  
  1812  angular.module('coreos-templates-svg', ['/coreos.svg/globe-only.svg', '/coreos.svg/icon-add.svg', '/coreos.svg/icon-back.svg', '/coreos.svg/icon-delete.svg', '/coreos.svg/icon-reboot.svg', '/coreos.svg/icon-right-arrow.svg', '/coreos.svg/logo.svg']);
  1813  
  1814  angular.module("/coreos.svg/globe-only.svg", []).run(["$templateCache", function($templateCache) {
  1815    $templateCache.put("/coreos.svg/globe-only.svg",
  1816      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
  1817      "<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\n" +
  1818      "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" +
  1819      "<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1820      "	 preserveAspectRatio=\"xMidYMin\" viewBox=\"0 0 222.068 222.068\" enable-background=\"new 0 0 222.068 222.068\"\n" +
  1821      "	 xml:space=\"preserve\">\n" +
  1822      "<g>\n" +
  1823      "	<path fill=\"#54A3DA\" d=\"M110.804,3.163c-59.27,0-107.479,48.212-107.479,107.473c0,59.265,48.209,107.474,107.479,107.474\n" +
  1824      "		c59.252,0,107.465-48.209,107.465-107.474C218.269,51.375,170.056,3.163,110.804,3.163z\"/>\n" +
  1825      "	<path fill=\"#F1616E\" d=\"M110.804,13.025c-17.283,0-31.941,27.645-37.235,66.069c-0.169,1.236-0.333,2.487-0.478,3.746\n" +
  1826      "		c-0.723,6.047-1.213,12.335-1.458,18.808c-0.117,2.962-0.175,5.956-0.175,8.988c0,3.029,0.058,6.029,0.175,8.985\n" +
  1827      "		c0.245,6.472,0.735,12.764,1.458,18.811c8.104,1.049,16.769,1.761,25.807,2.099c3.907,0.146,7.872,0.233,11.907,0.233\n" +
  1828      "		c4.023,0,8-0.088,11.895-0.233c9.049-0.338,17.708-1.05,25.819-2.099c0.892-0.114,1.77-0.239,2.659-0.368\n" +
  1829      "		c33.754-4.74,57.235-15.232,57.235-27.428C208.412,56.724,164.707,13.025,110.804,13.025z\"/>\n" +
  1830      "	<path fill=\"#FFFFFF\" d=\"M151.177,83.205c-0.979-1.428-2.029-2.796-3.148-4.11c-8.956-10.557-22.297-17.265-37.224-17.265\n" +
  1831      "		c-4.839,0-9.148,7.407-11.907,18.909c-1.096,4.586-1.947,9.819-2.495,15.498c-0.432,4.551-0.665,9.391-0.665,14.399\n" +
  1832      "		s0.233,9.849,0.665,14.396c4.554,0.432,9.387,0.664,14.402,0.664c5.009,0,9.842-0.232,14.396-0.664\n" +
  1833      "		c10.011-0.95,18.653-2.875,24.775-5.411c6.046-2.501,9.624-5.615,9.624-8.985C159.599,100.468,156.494,91.024,151.177,83.205z\"/>\n" +
  1834      "</g>\n" +
  1835      "</svg>\n" +
  1836      "");
  1837  }]);
  1838  
  1839  angular.module("/coreos.svg/icon-add.svg", []).run(["$templateCache", function($templateCache) {
  1840    $templateCache.put("/coreos.svg/icon-add.svg",
  1841      "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1842      "  preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 72.556 61\" enable-background=\"new 0 0 72.556 61\" xml:space=\"preserve\">\n" +
  1843      "  <path d=\"M34.521,8v11.088v23v10.737c0,2.209,1.791,4,4,4c2.209,0,4-1.791,4-4V42.067V19.109V8c0-2.209-1.791-4-4-4\n" +
  1844      "  C36.312,4,34.521,5.791,34.521,8z\"/>\n" +
  1845      "  <path d=\"M16.109,34.412h11.088h23h10.737c2.209,0,4-1.791,4-4c0-2.209-1.791-4-4-4H50.175H27.217H16.109c-2.209,0-4,1.791-4,4\n" +
  1846      "  C12.109,32.621,13.9,34.412,16.109,34.412z\"/>\n" +
  1847      "</svg>\n" +
  1848      "");
  1849  }]);
  1850  
  1851  angular.module("/coreos.svg/icon-back.svg", []).run(["$templateCache", function($templateCache) {
  1852    $templateCache.put("/coreos.svg/icon-back.svg",
  1853      "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1854      "  preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 73.356 61\" enable-background=\"new 0 0 73.356 61\" xml:space=\"preserve\">\n" +
  1855      "  <path d=\"M5.27,33.226l22.428,22.428c1.562,1.562,4.095,1.562,5.657,0c1.562-1.562,1.562-4.095,0-5.657L17.77,34.413h48.514\n" +
  1856      "  c2.209,0,4-1.791,4-4s-1.791-4-4-4H17.749l15.604-15.582c1.563-1.561,1.565-4.094,0.004-5.657C32.576,4.391,31.552,4,30.527,4\n" +
  1857      "  c-1.023,0-2.046,0.39-2.827,1.169L5.272,27.567c-0.751,0.75-1.173,1.768-1.173,2.829C4.098,31.458,4.52,32.476,5.27,33.226z\"/>\n" +
  1858      "</svg>\n" +
  1859      "");
  1860  }]);
  1861  
  1862  angular.module("/coreos.svg/icon-delete.svg", []).run(["$templateCache", function($templateCache) {
  1863    $templateCache.put("/coreos.svg/icon-delete.svg",
  1864      "<svg version=\"1.1\" fill=\"#f00\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n" +
  1865      "  x=\"0px\" y=\"0px\" preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 76.143 61\" enable-background=\"new 0 0 76.143 61\" xml:space=\"preserve\">\n" +
  1866      "  <path d=\"M49.41,13.505l-6.035,6.035L27.112,35.803l-6.035,6.035c-1.562,1.562-1.562,4.095,0,5.657c1.562,1.562,4.095,1.562,5.657,0\n" +
  1867      "  l6.05-6.05l16.234-16.234l6.05-6.05c1.562-1.562,1.562-4.095,0-5.657C53.505,11.943,50.972,11.943,49.41,13.505z\"/>\n" +
  1868      "  <path d=\"M21.077,19.162l6.035,6.035L43.375,41.46l6.035,6.035c1.562,1.562,4.095,1.562,5.657,0c1.562-1.562,1.562-4.095,0-5.657\n" +
  1869      "  l-6.05-6.05L32.783,19.555l-6.05-6.05c-1.562-1.562-4.095-1.562-5.657,0C19.515,15.067,19.515,17.6,21.077,19.162z\"/>\n" +
  1870      "</svg>\n" +
  1871      "");
  1872  }]);
  1873  
  1874  angular.module("/coreos.svg/icon-reboot.svg", []).run(["$templateCache", function($templateCache) {
  1875    $templateCache.put("/coreos.svg/icon-reboot.svg",
  1876      "<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1877      "	 preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 65.947 65.41\" enable-background=\"new 0 0 65.947 65.41\" xml:space=\"preserve\">\n" +
  1878      "<g>\n" +
  1879      "	<path d=\"M22.014,15.949c2.428-1.575,5.211-2.632,8.205-3.03c0,0,1.846-0.106,2.797-0.097C44.113,12.932,53.022,22,52.954,33.088\n" +
  1880      "		l11.226-1.075C63.884,19.558,56.337,8.875,45.553,4.081c-0.043-0.025-0.07-0.061-0.115-0.08c-3.756-1.645-7.896-2.578-12.25-2.621\n" +
  1881      "		c-0.014,0-0.025,0.002-0.039,0.002c-0.006,0-0.012-0.002-0.02-0.002c-0.691-0.006-1.371,0.021-2.051,0.066\n" +
  1882      "		c-0.475,0.026-0.941,0.073-1.414,0.12c-0.072,0.008-0.148,0.011-0.221,0.02v0.006c-5.494,0.601-10.578,2.603-14.848,5.678\n" +
  1883      "		l-3.068-4.523L7.038,21.636l18.849-2.034L22.014,15.949z\"/>\n" +
  1884      "	<path d=\"M44.204,48.517c-2.428,1.575-5.211,2.632-8.205,3.03c0,0-1.846,0.106-2.797,0.097c-11.098-0.11-20.007-9.178-19.938-20.266\n" +
  1885      "		L2.038,32.454c0.296,12.454,7.843,23.138,18.627,27.932c0.043,0.025,0.07,0.06,0.115,0.08c3.756,1.644,7.896,2.578,12.25,2.621\n" +
  1886      "		c0.014,0,0.025-0.002,0.039-0.002c0.006,0,0.012,0.002,0.02,0.002c0.691,0.006,1.371-0.021,2.051-0.065\n" +
  1887      "		c0.475-0.026,0.941-0.073,1.414-0.12c0.072-0.008,0.148-0.011,0.221-0.02v-0.006c5.494-0.601,10.578-2.604,14.848-5.678\n" +
  1888      "		l3.068,4.523L59.18,42.83l-18.849,2.034L44.204,48.517z\"/>\n" +
  1889      "</g>\n" +
  1890      "</svg>\n" +
  1891      "");
  1892  }]);
  1893  
  1894  angular.module("/coreos.svg/icon-right-arrow.svg", []).run(["$templateCache", function($templateCache) {
  1895    $templateCache.put("/coreos.svg/icon-right-arrow.svg",
  1896      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
  1897      "<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\n" +
  1898      "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" +
  1899      "<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1900      "	 width=\"6px\" height=\"10px\" viewBox=\"0 0 6 10\" enable-background=\"new 0 0 6 10\" xml:space=\"preserve\">\n" +
  1901      "<g>\n" +
  1902      "	<polygon fill=\"#333333\" points=\"0,0 0,10 6,5 	\"/>\n" +
  1903      "</g>\n" +
  1904      "</svg>\n" +
  1905      "");
  1906  }]);
  1907  
  1908  angular.module("/coreos.svg/logo.svg", []).run(["$templateCache", function($templateCache) {
  1909    $templateCache.put("/coreos.svg/logo.svg",
  1910      "<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1911      "    preserveAspectRatio=\"xMidYMin\" height=\"30px\" viewBox=\"24.5 40.5 744 224\" enable-background=\"new 24.5 40.5 744 224\" xml:space=\"preserve\">\n" +
  1912      "  <g>\n" +
  1913      "    <g>\n" +
  1914      "      <path fill=\"#53A3DA\" d=\"M136.168,45.527C76.898,45.527,28.689,93.739,28.689,153c0,59.265,48.209,107.474,107.479,107.474\n" +
  1915      "        c59.252,0,107.465-48.209,107.465-107.474C243.633,93.739,195.42,45.527,136.168,45.527z\"/>\n" +
  1916      "      <path fill=\"#F1606D\" d=\"M136.168,55.389c-17.283,0-31.941,27.645-37.235,66.069c-0.169,1.236-0.333,2.487-0.478,3.746\n" +
  1917      "        c-0.723,6.047-1.213,12.335-1.458,18.808c-0.117,2.962-0.175,5.956-0.175,8.988c0,3.029,0.058,6.029,0.175,8.985\n" +
  1918      "        c0.245,6.472,0.735,12.764,1.458,18.811c8.104,1.049,16.769,1.761,25.807,2.099c3.907,0.146,7.872,0.233,11.907,0.233\n" +
  1919      "        c4.023,0,8-0.088,11.895-0.233c9.049-0.338,17.708-1.05,25.819-2.099c0.892-0.114,1.77-0.239,2.659-0.368\n" +
  1920      "        c33.754-4.74,57.235-15.232,57.235-27.428C233.776,99.088,190.071,55.389,136.168,55.389z\"/>\n" +
  1921      "      <path fill=\"#FFFFFF\" d=\"M176.541,125.569c-0.979-1.428-2.029-2.796-3.148-4.11c-8.956-10.557-22.297-17.265-37.224-17.265\n" +
  1922      "        c-4.839,0-9.148,7.407-11.907,18.909c-1.096,4.586-1.947,9.819-2.495,15.498c-0.432,4.551-0.665,9.391-0.665,14.399\n" +
  1923      "        s0.233,9.849,0.665,14.396c4.554,0.432,9.387,0.664,14.402,0.664c5.009,0,9.842-0.232,14.396-0.664\n" +
  1924      "        c10.011-0.95,18.653-2.875,24.775-5.411c6.046-2.501,9.624-5.615,9.624-8.985C184.963,142.832,181.858,133.388,176.541,125.569z\"\n" +
  1925      "        />\n" +
  1926      "    </g>\n" +
  1927      "    <g>\n" +
  1928      "      <path fill=\"#231F20\" d=\"M344.891,100.053c12.585,0,22.816,6.138,29.262,13.062l-10.064,11.326\n" +
  1929      "        c-5.353-5.192-11.175-8.495-19.041-8.495c-16.839,0-28.953,14.16-28.953,37.291c0,23.448,11.169,37.608,28.32,37.608\n" +
  1930      "        c9.128,0,15.895-3.775,21.717-10.228l10.067,11.169c-8.335,9.598-19.038,14.95-32.099,14.95c-26.119,0-46.731-18.88-46.731-53.025\n" +
  1931      "        C297.37,120.036,318.454,100.053,344.891,100.053z\"/>\n" +
  1932      "      <path fill=\"#231F20\" d=\"M416.961,125.701c19.352,0,36.822,14.793,36.822,40.597c0,25.647-17.471,40.439-36.822,40.439\n" +
  1933      "        c-19.197,0-36.66-14.792-36.66-40.439C380.301,140.494,397.764,125.701,416.961,125.701z M416.961,191.945\n" +
  1934      "        c11.33,0,18.25-10.228,18.25-25.647c0-15.577-6.92-25.804-18.25-25.804s-18.094,10.227-18.094,25.804\n" +
  1935      "        C398.867,181.717,405.631,191.945,416.961,191.945z\"/>\n" +
  1936      "      <path fill=\"#231F20\" d=\"M459.771,127.589h14.943l1.26,13.688h0.629c5.506-10.07,13.691-15.577,21.871-15.577\n" +
  1937      "        c3.938,0,6.455,0.472,8.811,1.574l-3.148,15.734c-2.67-0.784-4.717-1.257-8.018-1.257c-6.139,0-13.539,4.245-18.256,15.893v47.203\n" +
  1938      "        h-18.092L459.771,127.589L459.771,127.589z\"/>\n" +
  1939      "      <path fill=\"#231F20\" d=\"M541.121,125.701c20.928,0,31.941,15.107,31.941,36.667c0,3.458-0.314,6.604-0.787,8.495h-49.09\n" +
  1940      "        c1.57,14.003,10.379,21.869,22.811,21.869c6.613,0,12.273-2.041,17.941-5.662l6.135,11.326\n" +
  1941      "        c-7.395,4.878-16.676,8.341-26.432,8.341c-21.404,0-38.08-14.95-38.08-40.439C505.561,141.12,523.023,125.701,541.121,125.701z\n" +
  1942      "         M557.326,159.376c0-12.277-5.189-19.671-15.732-19.671c-9.125,0-16.996,6.768-18.57,19.671H557.326z\"/>\n" +
  1943      "      <path fill=\"#F1606D\" d=\"M600.602,152.607c0-32.729,17.785-53.344,42.799-53.344c24.863,0,42.641,20.615,42.641,53.344\n" +
  1944      "        c0,32.889-17.777,54.13-42.641,54.13C618.387,206.737,600.602,185.496,600.602,152.607z M678.49,152.607\n" +
  1945      "        c0-28.639-14.158-46.731-35.09-46.731c-21.084,0-35.248,18.093-35.248,46.731c0,28.796,14.164,47.521,35.248,47.521\n" +
  1946      "        C664.332,200.128,678.49,181.403,678.49,152.607z\"/>\n" +
  1947      "      <path fill=\"#53A4D9\" d=\"M699.738,186.125c7.557,8.495,18.412,14.003,30.529,14.003c15.732,0,25.807-8.499,25.807-20.767\n" +
  1948      "        c0-12.904-8.494-17.154-18.723-21.717l-15.736-7.082c-8.969-3.936-20.934-10.385-20.934-25.808\n" +
  1949      "        c0-14.947,12.904-25.492,30.059-25.492c12.588,0,22.658,5.665,28.949,12.435l-4.244,4.878c-5.982-6.452-14.32-10.7-24.705-10.7\n" +
  1950      "        c-13.691,0-22.816,7.239-22.816,18.565c0,11.962,10.385,16.521,17.936,19.985l15.738,6.921\n" +
  1951      "        c11.486,5.195,21.713,11.647,21.713,27.539s-13.061,27.851-33.201,27.851c-15.107,0-26.75-6.451-34.932-15.576L699.738,186.125z\"\n" +
  1952      "        />\n" +
  1953      "    </g>\n" +
  1954      "  </g>\n" +
  1955      "</svg>\n" +
  1956      "");
  1957  }]);