coreos.js 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957
  1. 'use strict';
  2. angular.module('underscore', []).factory('_', function($window) {
  3. return $window._;
  4. });
  5. angular.module('jquery', []).factory('$', function($window) {
  6. return $window.$;
  7. });
  8. angular.module('d3', []).factory('d3', function($window) {
  9. return $window.d3;
  10. });
  11. angular.module('coreos.services', [
  12. 'coreos.events',
  13. 'underscore',
  14. 'jquery'
  15. ]);
  16. angular.module('coreos.ui', [
  17. 'coreos.events',
  18. 'underscore',
  19. 'jquery',
  20. 'd3',
  21. 'ui.bootstrap'
  22. ]);
  23. angular.module('coreos.filters', []);
  24. angular.module('coreos.events', []);
  25. angular.module('coreos', [
  26. 'coreos.events',
  27. 'coreos.services',
  28. 'coreos.ui',
  29. 'coreos.filters',
  30. 'coreos-templates-html',
  31. 'coreos-templates-svg',
  32. // External deps.
  33. 'ngRoute',
  34. 'ngResource',
  35. 'ngAnimate',
  36. 'ui.bootstrap',
  37. 'underscore',
  38. 'jquery',
  39. 'd3'
  40. ])
  41. .config(function($compileProvider) {
  42. // Allow irc links.
  43. $compileProvider
  44. .aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|irc):/);
  45. });
  46. 'use strict';
  47. angular.module('coreos.filters')
  48. .filter('orderObjectBy', function() {
  49. return function(items, field, reverse) {
  50. var filtered = [];
  51. angular.forEach(items, function(item) {
  52. filtered.push(item);
  53. });
  54. filtered.sort(function (a, b) {
  55. return (a[field] > b[field]);
  56. });
  57. if (reverse) {
  58. filtered.reverse();
  59. }
  60. return filtered;
  61. };
  62. });
  63. 'use strict';
  64. angular.module('coreos.filters')
  65. .filter('utc', function(_) {
  66. function convertToUtc(date) {
  67. return new Date(date.getUTCFullYear(),
  68. date.getUTCMonth(),
  69. date.getUTCDate(),
  70. date.getUTCHours(),
  71. date.getUTCMinutes(),
  72. date.getUTCSeconds());
  73. }
  74. return function(input) {
  75. if (_.isNumber(input)) {
  76. return convertToUtc(new Date(input));
  77. }
  78. if (_.isString(input)) {
  79. return convertToUtc(new Date(Date.parse(input)));
  80. }
  81. if (_.isDate(input)) {
  82. return convertToUtc(input);
  83. }
  84. return '';
  85. };
  86. });
  87. /**
  88. * Broadcast when the window size breakpoints change.
  89. * TODO(sym3tri): change implementation to use window.matchMedia instead.
  90. */
  91. 'use strict';
  92. angular.module('coreos.services')
  93. .factory('breakpointSvc', function(_, $window, $rootScope, CORE_CONST,
  94. CORE_EVENT) {
  95. var previousName;
  96. function getSize() {
  97. var width = $window.innerWidth;
  98. return _.find(CORE_CONST.BREAKPOINTS, function(bp) {
  99. if (bp.min <= width && bp.max > width) {
  100. return true;
  101. }
  102. }).name;
  103. }
  104. function onResize() {
  105. var breakpointName = getSize();
  106. if (breakpointName !== previousName) {
  107. $rootScope.$broadcast(CORE_EVENT.BREAKPOINT, breakpointName);
  108. previousName = breakpointName;
  109. }
  110. }
  111. // Broadcast initial size.
  112. $rootScope.$broadcast(CORE_EVENT.BREAKPOINT, getSize());
  113. // Watch for resizes.
  114. angular.element($window).on('resize', _.debounce(onResize, 20, true));
  115. return {
  116. getSize: getSize
  117. };
  118. });
  119. 'use strict';
  120. angular.module('coreos.services').provider('configSvc', function() {
  121. var configValues = {};
  122. this.config = function(newConfig) {
  123. if (newConfig) {
  124. configValues = newConfig;
  125. } else {
  126. return configValues;
  127. }
  128. };
  129. this.$get = function() {
  130. return {
  131. get: function(key) {
  132. if (key) {
  133. return configValues[key];
  134. } else {
  135. return angular.copy(configValues);
  136. }
  137. },
  138. set: function(key, value) {
  139. configValues[key] = value;
  140. }
  141. };
  142. };
  143. });
  144. 'use strict';
  145. angular.module('coreos').constant('CORE_CONST', {
  146. HIGHLIGHT_CSS_CLASS: 'co-an-highlight',
  147. BREAKPOINTS: [
  148. {
  149. name: 'xs',
  150. min: 0,
  151. max: 480
  152. },
  153. {
  154. name: 'sm',
  155. min: 480,
  156. max: 768
  157. },
  158. {
  159. name: 'md',
  160. min: 768,
  161. max: 992
  162. },
  163. {
  164. name: 'lg',
  165. min: 992,
  166. max: 1200
  167. },
  168. {
  169. name: 'xl',
  170. min: 1200,
  171. max: Infinity
  172. }
  173. ]
  174. });
  175. /**
  176. * @fileoverview
  177. *
  178. * Service for working with cookies since angular's built-in cookie service
  179. * leaves much to be desired.
  180. */
  181. 'use strict';
  182. angular.module('coreos.services').factory('cookieSvc',
  183. function($window, timeSvc) {
  184. return {
  185. /**
  186. * Create a new cookie.
  187. */
  188. create: function(name, value, daysUtilExpires) {
  189. var date, expires;
  190. if (daysUtilExpires) {
  191. date = new Date();
  192. date.setTime(date.getTime() +
  193. (daysUtilExpires * timeSvc.ONE_DAY_IN_MS));
  194. expires = '; expires=' + date.toGMTString();
  195. }
  196. else {
  197. expires = '';
  198. }
  199. $window.document.cookie = name + '=' + value + expires + '; path=/';
  200. },
  201. /**
  202. * Retrieve a cookie by name.
  203. */
  204. get: function(name) {
  205. var nameEq, cookieList, i, cookieStr;
  206. nameEq = name + '=';
  207. cookieList = $window.document.cookie.split(';');
  208. for (i = 0; i < cookieList.length; i++) {
  209. cookieStr = cookieList[i];
  210. while (cookieStr.charAt(0) === ' ') {
  211. cookieStr = cookieStr.substring(1, cookieStr.length);
  212. }
  213. if (cookieStr.indexOf(nameEq) === 0) {
  214. return cookieStr.substring(nameEq.length, cookieStr.length);
  215. }
  216. }
  217. return null;
  218. },
  219. /**
  220. * Delete a cookie by name.
  221. */
  222. remove: function(name) {
  223. this.create(name, '', -1);
  224. }
  225. };
  226. });
  227. /**
  228. * @fileoverview
  229. *
  230. * Simply inject this service to start broadcasting events.
  231. * It will feature-detect any available browser visibility api.
  232. * If the feature exists it will broadcast an event when browser visibiltiy
  233. * changes.
  234. */
  235. 'use strict';
  236. angular.module('coreos.services')
  237. .factory('documentVisibilitySvc', function($rootScope, $document, _,
  238. CORE_EVENT) {
  239. var document = $document[0],
  240. features,
  241. detectedFeature;
  242. function broadcastChangeEvent() {
  243. $rootScope.$broadcast(CORE_EVENT.DOC_VISIBILITY_CHANGE,
  244. document[detectedFeature.propertyName]);
  245. }
  246. features = {
  247. standard: {
  248. eventName: 'visibilitychange',
  249. propertyName: 'hidden'
  250. },
  251. moz: {
  252. eventName: 'mozvisibilitychange',
  253. propertyName: 'mozHidden'
  254. },
  255. ms: {
  256. eventName: 'msvisibilitychange',
  257. propertyName: 'msHidden'
  258. },
  259. webkit: {
  260. eventName: 'webkitvisibilitychange',
  261. propertyName: 'webkitHidden'
  262. }
  263. };
  264. Object.keys(features).some(function(feature) {
  265. if (_.isBoolean(document[features[feature].propertyName])) {
  266. detectedFeature = features[feature];
  267. return true;
  268. }
  269. });
  270. if (detectedFeature) {
  271. $document.on(detectedFeature.eventName, broadcastChangeEvent);
  272. }
  273. return {
  274. /**
  275. * Is the window currently hidden or not.
  276. */
  277. isHidden: function() {
  278. if (detectedFeature) {
  279. return document[detectedFeature.propertyName];
  280. }
  281. }
  282. };
  283. });
  284. 'use strict';
  285. angular.module('coreos.events').constant('CORE_EVENT', {
  286. PAGE_NOT_FOUND: 'core.event.page_not_found',
  287. BREAKPOINT: 'core.event.breakpoint',
  288. RESP_ERROR: 'core.event.resp_error',
  289. RESP_MUTATE: 'core.event.resp_mutate',
  290. DOC_VISIBILITY_CHANGE: 'core.event.doc_visibility_change',
  291. POLL_ERROR: 'core.event.poll_error'
  292. });
  293. /**
  294. * @fileoverview
  295. *
  296. * Utility service to highlight an element or selection of elements.
  297. * NOTE: Expects a [HIGHLIGHT_CSS_CLASS] class to be defined in constants.
  298. */
  299. 'use strict';
  300. angular.module('coreos.services')
  301. .factory('highlighterSvc', function($timeout, $, CORE_CONST) {
  302. var pendingTimeout;
  303. return {
  304. /**
  305. * Highlight an element in the DOM.
  306. *
  307. * @param {String|Element} elemOrSelector
  308. */
  309. highlight: function(elemOrSelector) {
  310. var elem;
  311. if (!elemOrSelector) {
  312. return;
  313. }
  314. elem = $(elemOrSelector);
  315. if (elem.hasClass(CORE_CONST.HIGHLIGHT_CSS_CLASS)) {
  316. $timeout.cancel(pendingTimeout);
  317. elem.removeClass(CORE_CONST.HIGHLIGHT_CSS_CLASS);
  318. }
  319. elem.addClass(CORE_CONST.HIGHLIGHT_CSS_CLASS);
  320. pendingTimeout = $timeout(
  321. elem.removeClass.bind(elem, CORE_CONST.HIGHLIGHT_CSS_CLASS), 5000);
  322. }
  323. };
  324. });
  325. 'use strict';
  326. angular.module('coreos.services')
  327. .factory('interceptorErrorSvc', function($q, $rootScope, CORE_EVENT) {
  328. function parseMessage(rejection) {
  329. var errorMsg;
  330. if (rejection.config.description) {
  331. errorMsg = 'Error attempting: ' + rejection.config.description;
  332. } else {
  333. errorMsg = 'A network error occurred.';
  334. }
  335. return errorMsg;
  336. }
  337. return {
  338. /**
  339. * For every failing $http request: broadcast an error event.
  340. */
  341. 'responseError': function(rejection) {
  342. if (!rejection.config.supressNotifications) {
  343. $rootScope.$broadcast(CORE_EVENT.RESP_ERROR,
  344. rejection,
  345. parseMessage(rejection));
  346. }
  347. return $q.reject(rejection);
  348. }
  349. };
  350. });
  351. 'use strict';
  352. angular.module('coreos.services')
  353. .factory('interceptorMutateSvc', function($q, $rootScope, CORE_EVENT) {
  354. // Remove last path segement of a url.
  355. function removeLastPath(url) {
  356. var newUrl = url.split('/');
  357. newUrl.pop();
  358. newUrl = newUrl.join('/');
  359. return newUrl;
  360. }
  361. return {
  362. /**
  363. * For every successful mutating $http request broadcast the urls.
  364. * Useful for cache invalidation.
  365. */
  366. 'response': function(response) {
  367. var method = response.config.method,
  368. url = response.config.url,
  369. cacheKeys;
  370. if (method !== 'GET') {
  371. cacheKeys = [];
  372. cacheKeys.push(url);
  373. if (method !== 'POST') {
  374. cacheKeys.push(removeLastPath(url));
  375. }
  376. $rootScope.$broadcast(CORE_EVENT.RESP_MUTATE, response);
  377. }
  378. return response || $q.when(response);
  379. }
  380. };
  381. });
  382. /**
  383. * A general purpose polling service.
  384. *
  385. * Provide a series of options with callacks and this service will start a
  386. * poller for the task.
  387. *
  388. * On failure it will try up to `maxRetries`, then will be killed and callback
  389. * to the `catchMaxFail()` function if provided.
  390. *
  391. * Optionally pass in a `scope` associated with the poller to automatically
  392. * kill the poller when the scope is destroyed.
  393. *
  394. * Global settings for this provider can be configured in the app `config`
  395. * stage. Instance will override defaults if provided ot the `register()`
  396. * function.
  397. *
  398. * EXAMPLE USAGE:
  399. *
  400. * poller.register('myPoller', {
  401. * fn: functionToRunRepeadedly,
  402. * then: successCallback,
  403. * catch: errorCallback,
  404. * catchMaxFail: afterMaxFailuresCallback,
  405. * scope: $scope,
  406. * startIn: 0,
  407. * interval: 5000
  408. * });
  409. */
  410. 'use strict';
  411. angular.module('coreos.services').provider('pollerSvc', function() {
  412. var settings = {},
  413. pollers = {};
  414. /**
  415. * Update global settings for the provider.
  416. * @param {Object} newSettings
  417. */
  418. this.settings = function(newSettings) {
  419. if (newSettings) {
  420. settings = newSettings;
  421. } else {
  422. return settings;
  423. }
  424. };
  425. /**
  426. * The main factory method.
  427. * Dependencies are injected and is invoked by angular.
  428. */
  429. this.$get = function pollerFactory($q, $http, $timeout, _, CORE_EVENT) {
  430. /* jshint unused:false */
  431. function isRegistered(name) {
  432. return !!pollers[name];
  433. }
  434. /**
  435. * Schedule the `execute` function to run.
  436. * @param {Number} delay When to start in ms.
  437. */
  438. function schedule(name, executor, delay) {
  439. var poller = pollers[name];
  440. if (!poller || poller._errorCount > poller.maxRetries) {
  441. return;
  442. }
  443. poller._state = 'waiting';
  444. poller._timeoutPromise = $timeout(executor, delay);
  445. }
  446. /**
  447. * Wrap a function to prevent it from running if the current state
  448. * is "terminated".
  449. */
  450. function runIfActive(name, fn) {
  451. var poller = pollers[name];
  452. if (!poller) {
  453. return angular.noop;
  454. }
  455. return function() {
  456. if (poller._state !== 'terminated') {
  457. return fn.apply(null, arguments);
  458. }
  459. };
  460. }
  461. function killPoller(name) {
  462. var poller;
  463. if (!isRegistered(name)) {
  464. return;
  465. }
  466. poller = pollers[name];
  467. poller._state = 'terminated';
  468. // Cancel the interval timer.
  469. if (poller._timeoutPromise) {
  470. $timeout.cancel(poller._timeoutPromise);
  471. }
  472. // Remove the scope.$destroy handler.
  473. poller._unlistenDestroy();
  474. // Delete from the list.
  475. delete pollers[name];
  476. }
  477. /**
  478. * Create an executor function for a poller with the given name.
  479. */
  480. function createExecutor(name) {
  481. var poller = pollers[name];
  482. if (!poller) {
  483. return angular.noop;
  484. }
  485. /**
  486. * The main function that will be run on an interval for a poller.
  487. * This wraps the user-provided function, executes callbacks after
  488. * completion, and handles scheduling.
  489. */
  490. return function execute() {
  491. if (poller._paused) {
  492. schedule(name, poller._executor, poller.interval);
  493. return;
  494. }
  495. poller._state = 'executing';
  496. poller.fn()
  497. .then(runIfActive(name, function() {
  498. poller._state = 'success';
  499. poller._errorCount = 0;
  500. poller.then.apply(null, arguments);
  501. }))
  502. .catch(runIfActive(name, function() {
  503. var args;
  504. poller._state = 'error';
  505. poller._errorCount += 1;
  506. poller.catch.apply(null, arguments);
  507. if (poller._errorCount > poller.maxRetries) {
  508. args = _.toArray(arguments);
  509. args.unshift(name);
  510. poller.catchMaxFail.apply(null, args);
  511. killPoller(name);
  512. }
  513. }))
  514. .finally(runIfActive(name, function() {
  515. poller.finally.apply(null, arguments);
  516. schedule(name, poller._executor, poller.interval);
  517. }));
  518. };
  519. }
  520. return {
  521. /**
  522. * Determines if a poller is already registered by name.
  523. * @param {String} name
  524. * @return {Boolean}
  525. */
  526. isRegistered: isRegistered,
  527. /**
  528. * Register the promise in the index, and schedule it to start polling.
  529. *
  530. * @param {String} name The uniqe name to associate with the poller.
  531. * @param {Object} options
  532. */
  533. register: function(name, options) {
  534. // kill the old poller if one by same name already exists.
  535. if (isRegistered(name)) {
  536. this.kill(name);
  537. }
  538. // Initialize all poller options.
  539. _.defaults(options, settings, {
  540. startIn: 0,
  541. maxRetries: 0,
  542. catch: angular.noop,
  543. then: angular.noop,
  544. finally: angular.noop,
  545. catchMaxFail: function() {
  546. if (options.scope) {
  547. options.scope.$emit(CORE_EVENT.POLL_ERROR);
  548. }
  549. },
  550. _unlistenDestroy: angular.noop,
  551. _errorCount: 0,
  552. _state: 'starting'
  553. });
  554. if (options.scope) {
  555. // If a scope is provided, automatically kill the poller when the
  556. // scope is destroyed.
  557. options._unlistenDestroy =
  558. options.scope.$on('$destroy', this.kill.bind(this, name));
  559. // When scope is prvided automatically pause polling when tab
  560. // loses visability.
  561. // TODO: add pauseAll() function and move this to app.run()
  562. options.scope.$on(CORE_EVENT.DOC_VISIBILITY_CHANGE,
  563. function(e, isHidden) {
  564. if (isHidden) {
  565. options._paused = true;
  566. } else {
  567. options._paused = false;
  568. }
  569. });
  570. }
  571. // Keep track of the poller in the index.
  572. pollers[name] = options;
  573. // Generate the executor wrapper for the poller.
  574. options._executor = createExecutor(name);
  575. // Schedule the initial run of the poller.
  576. schedule(name, options._executor, options.startIn);
  577. },
  578. /**
  579. * Kill a poller by name and remove all references, callbacks, etc.
  580. * @param {String} name
  581. */
  582. kill: function(name) {
  583. killPoller(name);
  584. },
  585. /**
  586. * Kill all registered pollers.
  587. */
  588. killAll: function() {
  589. Object.keys(pollers).forEach(this.kill.bind(this));
  590. }
  591. };
  592. };
  593. });
  594. /**
  595. * @fileoverview
  596. *
  597. * Utility service that scrolls elements into view.
  598. */
  599. 'use strict';
  600. angular.module('coreos.services')
  601. .factory('scrollerSvc', function($timeout, $) {
  602. function scroll(elem) {
  603. elem.first()[0].scrollIntoView();
  604. }
  605. var scrollerSvc = {
  606. /**
  607. * Scroll to the element on the page with matching id.
  608. * Adds and removes highlight classes too.
  609. *
  610. * @param {String|Element} elemOrSelector
  611. */
  612. scrollTo: function(elemOrSelector) {
  613. var maxTries = 100,
  614. numTries = 0,
  615. interval = 10,
  616. elem;
  617. if (!elemOrSelector) {
  618. return;
  619. }
  620. // Wait for element to appear in DOM if it doesn't exist yet,
  621. // then scroll to it.
  622. function attemptScroll() {
  623. elem = $(elemOrSelector);
  624. if (numTries < maxTries) {
  625. if (!elem.length) {
  626. numTries++;
  627. $timeout(attemptScroll, interval);
  628. } else {
  629. scroll(elem);
  630. }
  631. }
  632. }
  633. $timeout(attemptScroll, 0);
  634. }
  635. };
  636. return scrollerSvc;
  637. });
  638. 'use strict';
  639. angular.module('coreos.services')
  640. .factory('arraySvc', function() {
  641. return {
  642. /**
  643. * Remove first occurance of an item from an array in-place.
  644. *
  645. * @param {Arrray} ary Array to mutate.
  646. * @param {*} item Array item to remove.
  647. * @return {Array} The input array.
  648. */
  649. remove: function(ary, item) {
  650. var index;
  651. if (!ary || !ary.length) {
  652. return [];
  653. }
  654. index = ary.indexOf(item);
  655. if (index > -1) {
  656. ary.splice(index, 1);
  657. }
  658. return ary;
  659. }
  660. };
  661. });
  662. 'use strict';
  663. angular.module('coreos.services')
  664. .factory('mathSvc', function(_) {
  665. return {
  666. /**
  667. * If passed an array sums all items in the array.
  668. * Otherwise sums all arguments together.
  669. *
  670. * @param {Array|Number...}
  671. * @return {Number}
  672. */
  673. sum: function() {
  674. var ary;
  675. if (_.isArray(arguments[0])) {
  676. ary = arguments[0];
  677. } else {
  678. ary = _.toArray(arguments);
  679. }
  680. return ary.reduce(function(prev, curr) {
  681. return prev + curr;
  682. }, 0);
  683. }
  684. };
  685. });
  686. 'use strict';
  687. angular.module('coreos.services')
  688. .factory('timeSvc', function(_) {
  689. var ONE_MINUTE_IN_MS = 60 * 1000,
  690. ONE_HOUR_IN_MS = ONE_MINUTE_IN_MS * 60,
  691. ONE_DAY_IN_MS = ONE_HOUR_IN_MS * 24,
  692. ONE_WEEK_IN_MS = ONE_DAY_IN_MS * 7,
  693. THIRTY_DAYS_IN_MS = ONE_DAY_IN_MS * 30;
  694. function getTimestamp(val) {
  695. if (val && _.isNumber(val)) {
  696. return val;
  697. }
  698. return Date.now();
  699. }
  700. return {
  701. ONE_MINUTE_IN_MS: ONE_MINUTE_IN_MS,
  702. ONE_HOUR_IN_MS: ONE_HOUR_IN_MS,
  703. ONE_DAY_IN_MS: ONE_DAY_IN_MS,
  704. ONE_WEEK_IN_MS: ONE_WEEK_IN_MS,
  705. THIRTY_DAYS_IN_MS: THIRTY_DAYS_IN_MS,
  706. milliToSecs: function(ms) {
  707. return Math.floor(ms / 1000);
  708. },
  709. secsToMins: function(secs) {
  710. return Math.floor(parseInt(secs, 10) / 60) || 0;
  711. },
  712. minsToSecs: function(mins) {
  713. return Math.abs(parseInt(mins, 10) * 60) || 0;
  714. },
  715. oneHourAgo: function(ts) {
  716. return getTimestamp(ts) - this.ONE_HOUR_IN_MS;
  717. },
  718. oneDayAgo: function(ts) {
  719. return getTimestamp(ts) - this.ONE_DAY_IN_MS;
  720. },
  721. oneWeekAgo: function(ts) {
  722. return getTimestamp(ts) - this.ONE_WEEK_IN_MS;
  723. },
  724. thirtyDaysAgo: function(ts) {
  725. return getTimestamp(ts) - this.THIRTY_DAYS_IN_MS;
  726. },
  727. getRelativeTimestamp: function(term) {
  728. var now = Date.now();
  729. switch(term) {
  730. case 'month':
  731. return this.thirtyDaysAgo(now);
  732. case 'week':
  733. return this.oneWeekAgo(now);
  734. case 'day':
  735. return this.oneDayAgo(now);
  736. case 'hour':
  737. return this.oneHourAgo(now);
  738. }
  739. }
  740. };
  741. });
  742. /**
  743. * @fileoverview
  744. * Wrap buttons and automatically enable/disbale and show loading indicator.
  745. */
  746. 'use strict';
  747. angular.module('coreos.ui')
  748. .directive('coBtnBar', function($, $timeout, $compile) {
  749. return {
  750. templateUrl: '/coreos.ui/btn-bar/btn-bar.html',
  751. restrict: 'EA',
  752. transclude: true,
  753. replace: true,
  754. scope: {
  755. // A promise that indicates completion of async operation.
  756. 'completePromise': '='
  757. },
  758. link: function(scope, elem) {
  759. var linkButton,
  760. loaderDirectiveEl;
  761. linkButton = $('.btn-link', elem).last();
  762. loaderDirectiveEl =
  763. angular.element('<co-inline-loader></co-inline-loader>');
  764. $compile(loaderDirectiveEl)(scope);
  765. function disableButtons() {
  766. elem.append(loaderDirectiveEl);
  767. $('button', elem).attr('disabled', 'disabled');
  768. linkButton.addClass('hidden');
  769. }
  770. function enableButtons() {
  771. loaderDirectiveEl.remove();
  772. $('button', elem).removeAttr('disabled');
  773. linkButton.removeClass('hidden');
  774. }
  775. scope.$watch('completePromise', function(completePromise) {
  776. if (completePromise) {
  777. // Force async execution so disabling the button won't prevent form
  778. // submission.
  779. $timeout(disableButtons, 0);
  780. completePromise.finally(function() {
  781. // Also enable buttons asynchronously in case the request completes
  782. // before disableButtons() runs.
  783. $timeout(enableButtons, 0);
  784. });
  785. }
  786. });
  787. }
  788. };
  789. });
  790. /**
  791. * Simple directive to navigate to a route when the
  792. * element is clicked on.
  793. */
  794. 'use strict';
  795. angular.module('coreos.ui')
  796. .directive('coClickNav', function($location) {
  797. return {
  798. restrict: 'A',
  799. link: function(scope, elem, attrs) {
  800. function onClickHandler(event) {
  801. $location.url(attrs.coClickNav);
  802. scope.$apply();
  803. event.preventDefault();
  804. event.stopPropagation();
  805. }
  806. elem.on('click', onClickHandler);
  807. elem.on('$destroy', function() {
  808. elem.off('click', onClickHandler);
  809. });
  810. }
  811. };
  812. });
  813. /**
  814. * @fileoverview
  815. * Display a cog icon and construct dropdown menu.
  816. */
  817. 'use strict';
  818. angular.module('coreos.ui')
  819. .directive('coCog', function() {
  820. return {
  821. templateUrl: '/coreos.ui/cog/cog.html',
  822. restrict: 'E',
  823. replace: true,
  824. scope: {
  825. 'apps': '=',
  826. 'options': '=',
  827. 'size': '@',
  828. 'anchor': '@'
  829. },
  830. link: function(scope, elem) {
  831. scope.clickHandler = function($event, option) {
  832. $event.stopPropagation();
  833. $event.preventDefault();
  834. if (option.callback) {
  835. option.callback();
  836. }
  837. elem.removeClass('open');
  838. };
  839. }
  840. };
  841. });
  842. 'use strict';
  843. angular.module('coreos.ui')
  844. .controller('ConfirmModalCtrl', function($scope, $modalInstance,
  845. executeFn, title, message, btnText, errorFormatter) {
  846. $scope.errorFormatter = errorFormatter;
  847. $scope.title = title;
  848. $scope.message = message;
  849. $scope.btnText = btnText || 'Confirm';
  850. $scope.execute = function() {
  851. $scope.requestPromise = executeFn(null, {
  852. supressNotifications: true
  853. })
  854. .then($modalInstance.close);
  855. };
  856. $scope.cancel = function() {
  857. $modalInstance.dismiss('cancel');
  858. };
  859. });
  860. /**
  861. * @fileoverview
  862. * An arc donut chart.
  863. */
  864. // TDOO(sym3tri): add hover text.
  865. 'use strict';
  866. angular.module('coreos.ui')
  867. .directive('coDonut', function(d3, _) {
  868. return {
  869. templateUrl: '/coreos.ui/donut/donut.html',
  870. transclude: true,
  871. restrict: 'E',
  872. replace: true,
  873. scope: {
  874. // The original source data to graph.
  875. percent: '=',
  876. color: '@'
  877. },
  878. controller: function($scope) {
  879. var outerRadius, circleWidth;
  880. $scope.width = $scope.height = 80;
  881. outerRadius = $scope.width / 2;
  882. circleWidth = 15;
  883. $scope.arc = d3.svg.arc()
  884. .innerRadius(outerRadius - circleWidth)
  885. .outerRadius(outerRadius)
  886. .startAngle(0);
  887. // Constant to turn percents into radian angles.
  888. $scope.tau = 2 * Math.PI;
  889. },
  890. link: function(scope, elem) {
  891. scope.isRendered = false;
  892. function render() {
  893. var endAngle = scope.tau, // 100%
  894. textColor = '#333',
  895. bgcolor = '#eee',
  896. color = scope.color || '#000',
  897. fontSize = 18;
  898. // Keep track of added DOM elements.
  899. scope.el = {};
  900. scope.el.svg = d3.select(elem.find('.co-m-gauge__content')[0])
  901. .append('svg')
  902. .attr('width', scope.width)
  903. .attr('height', scope.height)
  904. .append('g')
  905. .attr('transform',
  906. 'translate(' +
  907. scope.width / 2 + ',' +
  908. scope.height / 2 + ')');
  909. scope.el.text = scope.el.svg.append('text')
  910. .attr('fill', textColor)
  911. .attr('y', Math.floor(fontSize / 3))
  912. .attr('font-size', fontSize + 'px')
  913. .attr('text-anchor', 'middle');
  914. scope.el.arcGroup = scope.el.svg.append('g')
  915. .attr('transform', 'rotate(180)');
  916. scope.el.background = scope.el.arcGroup.append('path')
  917. .datum({
  918. endAngle: endAngle
  919. })
  920. .style('fill', bgcolor)
  921. .attr('d', scope.arc);
  922. scope.el.foreground = scope.el.arcGroup.append('path')
  923. .datum({
  924. endAngle: scope.tau * (scope.percent || 0)
  925. })
  926. .style('fill', color)
  927. .style('opacity', 0.8)
  928. .attr('d', scope.arc);
  929. scope.isRendered = true;
  930. }
  931. /**
  932. * Update the value of the donut chart.
  933. */
  934. function updateValue() {
  935. if (!_.isNumber(scope.percent)) {
  936. scope.el.text.text('?');
  937. return;
  938. }
  939. scope.el.text.text(Math.round(scope.percent * 100) + '%');
  940. scope.el.foreground.transition()
  941. .duration(750)
  942. .call(arcTween, scope.percent * scope.tau);
  943. }
  944. /**
  945. * Transition function to animate the arc.
  946. */
  947. function arcTween(transition, newAngle) {
  948. transition.attrTween('d', function(d) {
  949. var interpolate = d3.interpolate(d.endAngle, newAngle);
  950. return function(t) {
  951. d.endAngle = interpolate(t);
  952. return scope.arc(d);
  953. };
  954. });
  955. }
  956. /**
  957. * Cleanup.
  958. */
  959. elem.on('$destroy', function() {
  960. scope.el.svg.remove();
  961. });
  962. render();
  963. scope.$watch('percent', function() {
  964. if (scope.isRendered) {
  965. updateValue();
  966. }
  967. });
  968. }
  969. };
  970. });
  971. /**
  972. * @fileoverview
  973. * Displays a message based on a promise.
  974. */
  975. 'use strict';
  976. angular.module('coreos.ui')
  977. .provider('errorMessageSvc', function() {
  978. var formatters = {};
  979. this.registerFormatter = function(name, fn) {
  980. formatters[name] = fn;
  981. };
  982. this.$get = function() {
  983. return {
  984. getFormatter: function(name) {
  985. return formatters[name] || angular.noop;
  986. }
  987. };
  988. };
  989. })
  990. .directive('coErrorMessage', function(errorMessageSvc) {
  991. return {
  992. templateUrl: '/coreos.ui/error-message/error-message.html',
  993. restrict: 'E',
  994. replace: true,
  995. scope: {
  996. promise: '=',
  997. formatter: '@',
  998. customMessage: '@message'
  999. },
  1000. controller: function postLink($scope) {
  1001. $scope.show = false;
  1002. function handler(resp) {
  1003. if ($scope.formatter) {
  1004. $scope.message =
  1005. errorMessageSvc.getFormatter($scope.formatter)(resp);
  1006. } else if ($scope.customMessage) {
  1007. $scope.message = $scope.customMessage;
  1008. } else {
  1009. return;
  1010. }
  1011. $scope.show = true;
  1012. }
  1013. $scope.$watch('promise', function(promise) {
  1014. $scope.show = false;
  1015. if (promise && promise.catch) {
  1016. promise.catch(handler);
  1017. }
  1018. });
  1019. }
  1020. };
  1021. });
  1022. /**
  1023. * @fileoverview
  1024. * Inject favicons into the <head>.
  1025. * Only use on <head> tag.
  1026. */
  1027. 'use strict';
  1028. angular.module('coreos.ui')
  1029. .directive('coFavicons', function($compile, $rootScope, configSvc) {
  1030. /*jshint maxlen:false */
  1031. return {
  1032. restrict: 'A',
  1033. replace: true,
  1034. link: function postLink(scope, elem) {
  1035. var newScope = $rootScope.$new(),
  1036. htmlTemplate =
  1037. '<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{path}}/apple-touch-icon-144-precomposed.png">' +
  1038. '<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{{path}}/apple-touch-icon-114-precomposed.png">' +
  1039. '<link rel="apple-touch-icon-precomposed" sizes="72x72" href="{{path}}/apple-touch-icon-72-precomposed.png">' +
  1040. '<link rel="apple-touch-icon-precomposed" href="{{path}}/apple-touch-icon-57-precomposed.png">' +
  1041. '<link rel="shortcut icon" href="{{path}}/favicon.png">';
  1042. newScope.path = configSvc.get('libPath') + '/img';
  1043. elem.append($compile(htmlTemplate)(newScope));
  1044. }
  1045. };
  1046. });
  1047. /*
  1048. */
  1049. /**
  1050. * @fileoverview
  1051. * Standard CoreOS footer.
  1052. *
  1053. */
  1054. 'use strict';
  1055. angular.module('coreos.ui')
  1056. .directive('coFooter', function() {
  1057. return {
  1058. templateUrl: '/coreos.ui/footer/footer.html',
  1059. transclude: true,
  1060. restrict: 'E',
  1061. replace: true
  1062. };
  1063. })
  1064. .directive('coFooterLink', function() {
  1065. return {
  1066. templateUrl: '/coreos.ui/footer/footer-link.html',
  1067. transclude: true,
  1068. restrict: 'E',
  1069. replace: true,
  1070. scope: {
  1071. href: '@',
  1072. iconClass: '@'
  1073. }
  1074. };
  1075. })
  1076. /**
  1077. * Convenience wrapper for doing sticky footers.
  1078. */
  1079. .directive('coFooterWrapper', function() {
  1080. return {
  1081. templateUrl: '/coreos.ui/footer/footer-wrapper.html',
  1082. transclude: true,
  1083. restrict: 'E',
  1084. replace: true
  1085. };
  1086. });
  1087. /**
  1088. * @fileoverview
  1089. * Highlight an item when its bound data changes.
  1090. */
  1091. 'use strict';
  1092. angular.module('coreos.ui')
  1093. .directive('coHighlight', function(highlighterSvc) {
  1094. return {
  1095. restrict: 'A',
  1096. link: function(scope, elem, attrs) {
  1097. scope.$watch(attrs.coHighlight, function(newValue, oldValue) {
  1098. if (newValue !== oldValue) {
  1099. highlighterSvc.highlight(elem);
  1100. }
  1101. });
  1102. }
  1103. };
  1104. });
  1105. /**
  1106. * @fileoverview
  1107. *
  1108. * Inline loading indicator widget.
  1109. */
  1110. 'use strict';
  1111. angular.module('coreos.ui')
  1112. .directive('coInlineLoader', function() {
  1113. return {
  1114. templateUrl: '/coreos.ui/inline-loader/inline-loader.html',
  1115. restrict: 'E',
  1116. replace: true
  1117. };
  1118. });
  1119. /**
  1120. * @fileoverview
  1121. *
  1122. * Loading indicator that centers itself inside its parent.
  1123. */
  1124. 'use strict';
  1125. angular.module('coreos.ui')
  1126. .directive('coLoader', function() {
  1127. return {
  1128. templateUrl: '/coreos.ui/loader/loader.html',
  1129. restrict: 'E',
  1130. replace: true
  1131. };
  1132. });
  1133. /**
  1134. * @fileoverview
  1135. * Display page title with primary action link.
  1136. */
  1137. 'use strict';
  1138. angular.module('coreos.ui')
  1139. .directive('coNavTitle', function() {
  1140. return {
  1141. templateUrl: '/coreos.ui/nav-title/nav-title.html',
  1142. transclude: true,
  1143. restrict: 'E',
  1144. replace: true,
  1145. scope: {
  1146. title: '@'
  1147. }
  1148. };
  1149. });
  1150. /**
  1151. * @fileoverview
  1152. * Top navbar which inlcudes nav links.
  1153. */
  1154. 'use strict';
  1155. angular.module('coreos.ui')
  1156. .directive('coNavbar', function(configSvc) {
  1157. return {
  1158. templateUrl: '/coreos.ui/navbar/navbar.html',
  1159. transclude: true,
  1160. restrict: 'E',
  1161. replace: true,
  1162. controller: function($scope) {
  1163. $scope.config = configSvc.get();
  1164. $scope.isCollapsed = true;
  1165. }
  1166. };
  1167. })
  1168. /**
  1169. * Simple directive to create bootstrap friendly navbar links.
  1170. * Will automatically add the 'active' class based on the route.
  1171. */
  1172. .directive('coNavbarLink', function($location) {
  1173. return {
  1174. templateUrl: '/coreos.ui/navbar/navbar-link.html',
  1175. transclude: true,
  1176. restrict: 'E',
  1177. replace: true,
  1178. scope: {
  1179. // The path to link to.
  1180. 'href': '@'
  1181. },
  1182. link: function(scope) {
  1183. scope.isActive = function() {
  1184. return $location.path() === scope.href;
  1185. };
  1186. }
  1187. };
  1188. })
  1189. /**
  1190. * Optional dropdown menu to put in the right of the navbar.
  1191. */
  1192. .directive('coNavbarDropdown', function() {
  1193. return {
  1194. templateUrl: '/coreos.ui/navbar/navbar-dropdown.html',
  1195. transclude: true,
  1196. restrict: 'E',
  1197. replace: true,
  1198. scope: {
  1199. text: '@'
  1200. }
  1201. };
  1202. });
  1203. /**
  1204. * @fileoverview
  1205. * Directive to easily inline svg images.
  1206. * NOTE: kind of a hack to get ng-include to work properly within a directive
  1207. * without wrapping it with an extra DOM element.
  1208. */
  1209. 'use strict';
  1210. angular.module('coreos.ui')
  1211. .directive('coSvg', function($, $rootScope, $compile) {
  1212. return {
  1213. template: '<div></div>',
  1214. restrict: 'E',
  1215. replace: true,
  1216. scope: {
  1217. src: '@',
  1218. width: '@',
  1219. height: '@'
  1220. },
  1221. link: function(scope, elem, attrs) {
  1222. var containerEl, html, newScope;
  1223. newScope = $rootScope.$new();
  1224. html = '<div class="co-m-svg" '+
  1225. 'ng-class="classes" ng-style="style" ng-include="src"></div>';
  1226. newScope.style = {};
  1227. if (scope.width) {
  1228. newScope.style.width = scope.width + 'px';
  1229. }
  1230. if (scope.height) {
  1231. newScope.style.height = scope.height + 'px';
  1232. }
  1233. if (attrs.class) {
  1234. newScope.classes = attrs.class;
  1235. }
  1236. scope.$watch('src', function(src) {
  1237. if (src) {
  1238. newScope.src = src;
  1239. containerEl = $compile(html)(newScope);
  1240. elem.replaceWith(containerEl);
  1241. }
  1242. });
  1243. }
  1244. };
  1245. });
  1246. 'use strict';
  1247. angular.module('coreos.ui')
  1248. .directive('coTextCopy', function() {
  1249. return {
  1250. restrict: 'A',
  1251. replace: true,
  1252. link: function(scope, elem) {
  1253. function onClickHandler(event) {
  1254. elem.select();
  1255. event.preventDefault();
  1256. event.stopPropagation();
  1257. }
  1258. elem.on('click', onClickHandler);
  1259. elem.on('$destroy', function() {
  1260. elem.off('click', onClickHandler);
  1261. });
  1262. }
  1263. };
  1264. });
  1265. /**
  1266. * @fileoverview
  1267. *
  1268. * Keeps the title tag updated.
  1269. */
  1270. 'use strict';
  1271. angular.module('coreos.ui')
  1272. .directive('coTitle', function() {
  1273. return {
  1274. transclude: false,
  1275. restrict: 'A',
  1276. scope: {
  1277. suffix: '@coTitleSuffix'
  1278. },
  1279. controller: function($scope, $rootScope, $route) {
  1280. $scope.pageTitle = '';
  1281. $scope.defaultTitle = null;
  1282. $rootScope.$on('$routeChangeSuccess', function() {
  1283. $scope.pageTitle = $route.current.title || $route.current.$$route.title;
  1284. });
  1285. },
  1286. link: function(scope, elem) {
  1287. scope.$watch('pageTitle', function(title) {
  1288. if (title) {
  1289. if (!scope.defaultTitle) {
  1290. scope.defaultTitle = elem.text();
  1291. }
  1292. elem.text(title + ' ' + scope.suffix);
  1293. } else {
  1294. if (scope.defaultTitle) {
  1295. elem.text(scope.defaultTitle);
  1296. }
  1297. }
  1298. });
  1299. }
  1300. };
  1301. });
  1302. /**
  1303. * @fileoverview
  1304. * Directive to display global error or info messages.
  1305. * Enqueue messages through the toastSvc.
  1306. */
  1307. 'use strict';
  1308. angular.module('coreos.ui')
  1309. .directive('coToast', function() {
  1310. return {
  1311. templateUrl: '/coreos.ui/toast/toast.html',
  1312. restrict: 'E',
  1313. replace: true,
  1314. scope: true,
  1315. controller: function($scope, toastSvc) {
  1316. $scope.messages = toastSvc.messages;
  1317. $scope.dismiss = toastSvc.dismiss;
  1318. }
  1319. };
  1320. });
  1321. angular.module('coreos.services')
  1322. .factory('toastSvc', function($timeout) {
  1323. var AUTO_DISMISS_TIME = 5000,
  1324. service,
  1325. lastTimeoutPromise;
  1326. function dequeue() {
  1327. if (service.messages.length) {
  1328. service.messages.shift();
  1329. }
  1330. }
  1331. function enqueue(type, text) {
  1332. service.messages.push({
  1333. type: type,
  1334. text: text
  1335. });
  1336. lastTimeoutPromise = $timeout(dequeue, AUTO_DISMISS_TIME);
  1337. }
  1338. function cancelTimeout() {
  1339. if (lastTimeoutPromise) {
  1340. $timeout.cancel(lastTimeoutPromise);
  1341. }
  1342. }
  1343. service = {
  1344. messages: [],
  1345. error: enqueue.bind(null, 'error'),
  1346. info: enqueue.bind(null, 'info'),
  1347. dismiss: function(index) {
  1348. cancelTimeout();
  1349. service.messages.splice(index, 1);
  1350. },
  1351. dismissAll: function() {
  1352. cancelTimeout();
  1353. service.messages.length = 0;
  1354. }
  1355. };
  1356. return service;
  1357. });
  1358. 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']);
  1359. angular.module("/coreos.ui/btn-bar/btn-bar.html", []).run(["$templateCache", function($templateCache) {
  1360. $templateCache.put("/coreos.ui/btn-bar/btn-bar.html",
  1361. "<div class=\"co-m-btn-bar\" ng-transclude>\n" +
  1362. "</div>\n" +
  1363. "");
  1364. }]);
  1365. angular.module("/coreos.ui/cog/cog.html", []).run(["$templateCache", function($templateCache) {
  1366. $templateCache.put("/coreos.ui/cog/cog.html",
  1367. "<div class=\"co-m-cog\">\n" +
  1368. " <span class=\"co-m-cog__icon co-m-cog__icon--size-{{size}} dropdown-toggle fa fa-cog\"></span>\n" +
  1369. " <ul class=\"dropdown-menu co-m-cog__dropdown co-m-dropdown--dark co-m-cog__dropdown--anchor-{{anchor}}\">\n" +
  1370. " <li ng-repeat=\"option in options | orderBy:'weight'\">\n" +
  1371. " <a ng-if=\"option.href\" ng-href=\"{{option.href}}\">{{option.label}}</a>\n" +
  1372. " <a ng-if=\"!option.href\" ng-click=\"clickHandler($event, option)\">{{option.label}}</a>\n" +
  1373. " </li>\n" +
  1374. " </ul>\n" +
  1375. "</div>\n" +
  1376. "");
  1377. }]);
  1378. angular.module("/coreos.ui/confirm-modal/confirm-modal.html", []).run(["$templateCache", function($templateCache) {
  1379. $templateCache.put("/coreos.ui/confirm-modal/confirm-modal.html",
  1380. "<div>\n" +
  1381. " <form ng-submit=\"execute()\" name=\"form\" role=\"form\">\n" +
  1382. " <div class=\"modal-header\">\n" +
  1383. " <h4 class=\"modal-title\" ng-bind=\"title\"></h4>\n" +
  1384. " </div>\n" +
  1385. " <div class=\"modal-body\" ng-bind=\"message\"></div>\n" +
  1386. " <div class=\"modal-footer\" co-btn-bar complete-promise=\"requestPromise\">\n" +
  1387. " <co-error-message formatter=\"{{errorFormatter}}\" promise=\"requestPromise\"></co-error-message>\n" +
  1388. " <button type=\"submit\" class=\"btn btn-primary\" ng-bind=\"btnText\"></button>\n" +
  1389. " <button type=\"button\" ng-click=\"cancel()\" class=\"btn btn-link\">Cancel</button>\n" +
  1390. " </div>\n" +
  1391. " </form>\n" +
  1392. "</div>\n" +
  1393. "");
  1394. }]);
  1395. angular.module("/coreos.ui/donut/donut.html", []).run(["$templateCache", function($templateCache) {
  1396. $templateCache.put("/coreos.ui/donut/donut.html",
  1397. "<div class=\"co-m-donut co-m-gauge\">\n" +
  1398. " <div class=\"co-m-gauge__content\"></div>\n" +
  1399. " <div class=\"co-m-gauge__label\" ng-transclude></div>\n" +
  1400. "</div>\n" +
  1401. "");
  1402. }]);
  1403. angular.module("/coreos.ui/error-message/error-message.html", []).run(["$templateCache", function($templateCache) {
  1404. $templateCache.put("/coreos.ui/error-message/error-message.html",
  1405. "<div ng-show=\"show\" class=\"co-m-message co-m-message--error co-an-fade-in-out ng-hide\">{{message}}</div>\n" +
  1406. "");
  1407. }]);
  1408. angular.module("/coreos.ui/favicons/favicons.html", []).run(["$templateCache", function($templateCache) {
  1409. $templateCache.put("/coreos.ui/favicons/favicons.html",
  1410. "");
  1411. }]);
  1412. angular.module("/coreos.ui/footer/footer-link.html", []).run(["$templateCache", function($templateCache) {
  1413. $templateCache.put("/coreos.ui/footer/footer-link.html",
  1414. "<a class=\"co-m-footer-link\" href=\"{{href}}\">\n" +
  1415. " <span class=\"co-m-footer-link--icon\" ng-if=\"iconClass\" ng-class=\"iconClass\"></span>\n" +
  1416. " <span ng-transclude></span>\n" +
  1417. "</a>\n" +
  1418. "");
  1419. }]);
  1420. angular.module("/coreos.ui/footer/footer-wrapper.html", []).run(["$templateCache", function($templateCache) {
  1421. $templateCache.put("/coreos.ui/footer/footer-wrapper.html",
  1422. "<div id=\"co-l-footer-wrapper\">\n" +
  1423. " <div ng-transclude></div>\n" +
  1424. " <div id=\"co-l-footer-push\"></div>\n" +
  1425. "</div>\n" +
  1426. "");
  1427. }]);
  1428. angular.module("/coreos.ui/footer/footer.html", []).run(["$templateCache", function($templateCache) {
  1429. $templateCache.put("/coreos.ui/footer/footer.html",
  1430. "<div id=\"co-l-footer\">\n" +
  1431. " <div class=\"container\" ng-transclude></div>\n" +
  1432. "</div>\n" +
  1433. "");
  1434. }]);
  1435. angular.module("/coreos.ui/inline-loader/inline-loader.html", []).run(["$templateCache", function($templateCache) {
  1436. $templateCache.put("/coreos.ui/inline-loader/inline-loader.html",
  1437. "<div class=\"co-m-inline-loader co-an-fade-in-out\">\n" +
  1438. " <div class=\"co-m-inline-loader-dot__one\"></div>\n" +
  1439. " <div class=\"co-m-inline-loader-dot__two\"></div>\n" +
  1440. " <div class=\"co-m-inline-loader-dot__three\"></div>\n" +
  1441. "</div>\n" +
  1442. "");
  1443. }]);
  1444. angular.module("/coreos.ui/loader/loader.html", []).run(["$templateCache", function($templateCache) {
  1445. $templateCache.put("/coreos.ui/loader/loader.html",
  1446. "<div class=\"co-m-loader co-an-fade-in-out\">\n" +
  1447. " <span class=\"co-m-loader__spinner\"></span>\n" +
  1448. "</div>\n" +
  1449. "");
  1450. }]);
  1451. angular.module("/coreos.ui/nav-title/nav-title.html", []).run(["$templateCache", function($templateCache) {
  1452. $templateCache.put("/coreos.ui/nav-title/nav-title.html",
  1453. "<div class=\"co-m-nav-title row\">\n" +
  1454. " <div ng-transclude class=\"col-lg-3 col-md-3 col-sm-3 col-xs-6\"></div>\n" +
  1455. " <div class=\"col-lg-6 col-md-6 col-sm-6 col-xs-12\">\n" +
  1456. " <h1 class=\"co-m-page-title co-fx-text-shadow\">{{title}}</h1>\n" +
  1457. " </div>\n" +
  1458. "</div>\n" +
  1459. "");
  1460. }]);
  1461. angular.module("/coreos.ui/navbar/navbar-dropdown.html", []).run(["$templateCache", function($templateCache) {
  1462. $templateCache.put("/coreos.ui/navbar/navbar-dropdown.html",
  1463. "<ul class=\"nav navbar-nav pull-right\">\n" +
  1464. " <li class=\"dropdown pull-right\">\n" +
  1465. " <a href=\"#\" class=\"dropdown-toggle\">{{text}} <b class=\"caret\"></b></a>\n" +
  1466. " <ul ng-transclude class=\"dropdown-menu co-m-dropdown--dark\"></ul>\n" +
  1467. " </li>\n" +
  1468. "</ul>\n" +
  1469. "");
  1470. }]);
  1471. angular.module("/coreos.ui/navbar/navbar-link.html", []).run(["$templateCache", function($templateCache) {
  1472. $templateCache.put("/coreos.ui/navbar/navbar-link.html",
  1473. "<li class=\"co-m-nav-link\" ng-class=\"{'active': isActive()}\">\n" +
  1474. " <a ng-href=\"{{href}}\" ng-transclude></a>\n" +
  1475. "</li>\n" +
  1476. "");
  1477. }]);
  1478. angular.module("/coreos.ui/navbar/navbar.html", []).run(["$templateCache", function($templateCache) {
  1479. $templateCache.put("/coreos.ui/navbar/navbar.html",
  1480. "<div class=\"co-m-navbar co-fx-box-shadow navbar navbar-fixed-top\">\n" +
  1481. "\n" +
  1482. " <div class=\"navbar-header\">\n" +
  1483. " <button ng-click=\"isCollapsed = !isCollapsed\" class=\"navbar-toggle\" type=\"button\">\n" +
  1484. " <span class=\"glyphicon glyphicon-align-justify\"></span>\n" +
  1485. " </button>\n" +
  1486. " <a ng-href=\"{{config.siteBasePath}}\" class=\"navbar-brand\">\n" +
  1487. " <co-svg class=\"co-m-navbar__logo\" src=\"/coreos.svg/logo.svg\"></co-svg>\n" +
  1488. " </a>\n" +
  1489. " </div>\n" +
  1490. "\n" +
  1491. " <div collapse=\"isCollapsed\" ng-transclude class=\"collapse navbar-collapse\"></div>\n" +
  1492. "\n" +
  1493. "</div>\n" +
  1494. "");
  1495. }]);
  1496. angular.module("/coreos.ui/toast/toast.html", []).run(["$templateCache", function($templateCache) {
  1497. $templateCache.put("/coreos.ui/toast/toast.html",
  1498. "<div class=\"co-m-toast\">\n" +
  1499. " <div ng-repeat=\"message in messages\"\n" +
  1500. " class=\"co-m-toast__message co-m-message co-m-message--{{message.type}} co-an-fade-in-out co-fx-box-shadow\">\n" +
  1501. " {{message.text}}\n" +
  1502. " <span ng-click=\"dismiss($index)\" class=\"pull-right glyphicon glyphicon-remove text-right co-m-message__close\"></span>\n" +
  1503. " </div>\n" +
  1504. "</div>\n" +
  1505. "");
  1506. }]);
  1507. 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']);
  1508. angular.module("/coreos.svg/globe-only.svg", []).run(["$templateCache", function($templateCache) {
  1509. $templateCache.put("/coreos.svg/globe-only.svg",
  1510. "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
  1511. "<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->\n" +
  1512. "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" +
  1513. "<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" +
  1514. " preserveAspectRatio=\"xMidYMin\" viewBox=\"0 0 222.068 222.068\" enable-background=\"new 0 0 222.068 222.068\"\n" +
  1515. " xml:space=\"preserve\">\n" +
  1516. "<g>\n" +
  1517. " <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" +
  1518. " c59.252,0,107.465-48.209,107.465-107.474C218.269,51.375,170.056,3.163,110.804,3.163z\"/>\n" +
  1519. " <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" +
  1520. " 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" +
  1521. " 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" +
  1522. " 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" +
  1523. " c33.754-4.74,57.235-15.232,57.235-27.428C208.412,56.724,164.707,13.025,110.804,13.025z\"/>\n" +
  1524. " <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" +
  1525. " 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" +
  1526. " 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" +
  1527. " 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" +
  1528. "</g>\n" +
  1529. "</svg>\n" +
  1530. "");
  1531. }]);
  1532. angular.module("/coreos.svg/icon-add.svg", []).run(["$templateCache", function($templateCache) {
  1533. $templateCache.put("/coreos.svg/icon-add.svg",
  1534. "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1535. " preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 72.556 61\" enable-background=\"new 0 0 72.556 61\" xml:space=\"preserve\">\n" +
  1536. " <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" +
  1537. " C36.312,4,34.521,5.791,34.521,8z\"/>\n" +
  1538. " <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" +
  1539. " C12.109,32.621,13.9,34.412,16.109,34.412z\"/>\n" +
  1540. "</svg>\n" +
  1541. "");
  1542. }]);
  1543. angular.module("/coreos.svg/icon-back.svg", []).run(["$templateCache", function($templateCache) {
  1544. $templateCache.put("/coreos.svg/icon-back.svg",
  1545. "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"\n" +
  1546. " preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 73.356 61\" enable-background=\"new 0 0 73.356 61\" xml:space=\"preserve\">\n" +
  1547. " <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" +
  1548. " 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" +
  1549. " 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" +
  1550. "</svg>\n" +
  1551. "");
  1552. }]);
  1553. angular.module("/coreos.svg/icon-delete.svg", []).run(["$templateCache", function($templateCache) {
  1554. $templateCache.put("/coreos.svg/icon-delete.svg",
  1555. "<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" +
  1556. " x=\"0px\" y=\"0px\" preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 76.143 61\" enable-background=\"new 0 0 76.143 61\" xml:space=\"preserve\">\n" +
  1557. " <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" +
  1558. " 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" +
  1559. " <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" +
  1560. " 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" +
  1561. "</svg>\n" +
  1562. "");
  1563. }]);
  1564. angular.module("/coreos.svg/icon-reboot.svg", []).run(["$templateCache", function($templateCache) {
  1565. $templateCache.put("/coreos.svg/icon-reboot.svg",
  1566. "<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" +
  1567. " preserveAspectRatio=\"xMinYMin\" viewBox=\"0 0 65.947 65.41\" enable-background=\"new 0 0 65.947 65.41\" xml:space=\"preserve\">\n" +
  1568. "<g>\n" +
  1569. " <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" +
  1570. " 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" +
  1571. " 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" +
  1572. " 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" +
  1573. " l-3.068-4.523L7.038,21.636l18.849-2.034L22.014,15.949z\"/>\n" +
  1574. " <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" +
  1575. " 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" +
  1576. " 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" +
  1577. " 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" +
  1578. " l3.068,4.523L59.18,42.83l-18.849,2.034L44.204,48.517z\"/>\n" +
  1579. "</g>\n" +
  1580. "</svg>\n" +
  1581. "");
  1582. }]);
  1583. angular.module("/coreos.svg/icon-right-arrow.svg", []).run(["$templateCache", function($templateCache) {
  1584. $templateCache.put("/coreos.svg/icon-right-arrow.svg",
  1585. "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
  1586. "<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->\n" +
  1587. "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n" +
  1588. "<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" +
  1589. " width=\"6px\" height=\"10px\" viewBox=\"0 0 6 10\" enable-background=\"new 0 0 6 10\" xml:space=\"preserve\">\n" +
  1590. "<g>\n" +
  1591. " <polygon fill=\"#333333\" points=\"0,0 0,10 6,5 \"/>\n" +
  1592. "</g>\n" +
  1593. "</svg>\n" +
  1594. "");
  1595. }]);
  1596. angular.module("/coreos.svg/logo.svg", []).run(["$templateCache", function($templateCache) {
  1597. $templateCache.put("/coreos.svg/logo.svg",
  1598. "<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" +
  1599. " preserveAspectRatio=\"xMidYMin\" height=\"30px\" viewBox=\"24.5 40.5 744 224\" enable-background=\"new 24.5 40.5 744 224\" xml:space=\"preserve\">\n" +
  1600. " <g>\n" +
  1601. " <g>\n" +
  1602. " <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" +
  1603. " c59.252,0,107.465-48.209,107.465-107.474C243.633,93.739,195.42,45.527,136.168,45.527z\"/>\n" +
  1604. " <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" +
  1605. " 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" +
  1606. " 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" +
  1607. " 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" +
  1608. " c33.754-4.74,57.235-15.232,57.235-27.428C233.776,99.088,190.071,55.389,136.168,55.389z\"/>\n" +
  1609. " <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" +
  1610. " 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" +
  1611. " 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" +
  1612. " 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" +
  1613. " />\n" +
  1614. " </g>\n" +
  1615. " <g>\n" +
  1616. " <path fill=\"#231F20\" d=\"M344.891,100.053c12.585,0,22.816,6.138,29.262,13.062l-10.064,11.326\n" +
  1617. " 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" +
  1618. " 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" +
  1619. " C297.37,120.036,318.454,100.053,344.891,100.053z\"/>\n" +
  1620. " <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" +
  1621. " 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" +
  1622. " 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" +
  1623. " C398.867,181.717,405.631,191.945,416.961,191.945z\"/>\n" +
  1624. " <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" +
  1625. " 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" +
  1626. " h-18.092L459.771,127.589L459.771,127.589z\"/>\n" +
  1627. " <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" +
  1628. " 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" +
  1629. " 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" +
  1630. " 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" +
  1631. " <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" +
  1632. " 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" +
  1633. " 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" +
  1634. " C664.332,200.128,678.49,181.403,678.49,152.607z\"/>\n" +
  1635. " <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" +
  1636. " 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" +
  1637. " 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" +
  1638. " 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" +
  1639. " 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" +
  1640. " />\n" +
  1641. " </g>\n" +
  1642. " </g>\n" +
  1643. "</svg>\n" +
  1644. "");
  1645. }]);