helper.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var stringUtils = require('utils/string_utils');
  19. /**
  20. * Remove spaces at beginning and ending of line.
  21. * @example
  22. * var str = " I'm a string "
  23. * str.trim() // return "I'm a string"
  24. * @method trim
  25. * @return {string}
  26. */
  27. String.prototype.trim = function () {
  28. return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  29. };
  30. /**
  31. * Determines whether string end within another string.
  32. *
  33. * @method endsWith
  34. * @param suffix {string} substring for search
  35. * @return {boolean}
  36. */
  37. String.prototype.endsWith = function(suffix) {
  38. return this.indexOf(suffix, this.length - suffix.length) !== -1;
  39. };
  40. /**
  41. * Determines whether string start within another string.
  42. *
  43. * @method startsWith
  44. * @param prefix {string} substring for search
  45. * @return {boolean}
  46. */
  47. String.prototype.startsWith = function (prefix){
  48. return this.indexOf(prefix) == 0;
  49. };
  50. /**
  51. * Determines whether string founded within another string.
  52. *
  53. * @method contains
  54. * @param substring {string} substring for search
  55. * @return {boolean}
  56. */
  57. String.prototype.contains = function(substring) {
  58. return this.indexOf(substring) != -1;
  59. };
  60. /**
  61. * Capitalize the first letter of string.
  62. * @method capitalize
  63. * @return {string}
  64. */
  65. String.prototype.capitalize = function () {
  66. return this.charAt(0).toUpperCase() + this.slice(1);
  67. };
  68. /**
  69. * Capitalize the first letter of string.
  70. * And set to lowercase other part of string
  71. * @method toCapital
  72. * @return {string}
  73. */
  74. String.prototype.toCapital = function () {
  75. return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
  76. };
  77. /**
  78. * Finds the value in an object where this string is a key.
  79. * Optionally, the index of the key can be provided where the
  80. * value of the nth key in the hierarchy is returned.
  81. *
  82. * Example:
  83. * var tofind = 'smart';
  84. * var person = {'name': 'Bob Bob', 'smart': 'no', 'age': '28', 'personality': {'smart': 'yes', 'funny': 'yes', 'emotion': 'happy'} };
  85. * tofind.findIn(person); // 'no'
  86. * tofind.findIn(person, 0); // 'no'
  87. * tofind.findIn(person, 1); // 'yes'
  88. * tofind.findIn(person, 2); // null
  89. *
  90. * @method findIn
  91. * @param multi {object}
  92. * @param index {number} Occurrence count of this key
  93. * @return {*} Value of key at given index
  94. */
  95. String.prototype.findIn = function(multi, index, _foundValues) {
  96. if (!index) {
  97. index = 0;
  98. }
  99. if (!_foundValues) {
  100. _foundValues = [];
  101. }
  102. multi = multi || '';
  103. var value = null;
  104. var str = this.valueOf();
  105. if (typeof multi == 'object') {
  106. for ( var key in multi) {
  107. if (value != null) {
  108. break;
  109. }
  110. if (key == str) {
  111. _foundValues.push(multi[key]);
  112. }
  113. if (_foundValues.length - 1 == index) {
  114. // Found the value
  115. return _foundValues[index];
  116. }
  117. if (typeof multi[key] == 'object') {
  118. value = value || this.findIn(multi[key], index, _foundValues);
  119. }
  120. }
  121. }
  122. return value;
  123. };
  124. /**
  125. * Replace {i} with argument. where i is number of argument to replace with.
  126. * @example
  127. * var str = "{0} world{1}";
  128. * str.format("Hello", "!") // return "Hello world!"
  129. *
  130. * @method format
  131. * @return {string}
  132. */
  133. String.prototype.format = function () {
  134. var args = arguments;
  135. return this.replace(/{(\d+)}/g, function (match, number) {
  136. return typeof args[number] != 'undefined' ? args[number] : match;
  137. });
  138. };
  139. /**
  140. * Wrap words in string within template.
  141. *
  142. * @method highlight
  143. * @param {string[]} words - words to wrap
  144. * @param {string} [highlightTemplate="<b>{0}</b>"] - template for wrapping
  145. * @return {string}
  146. */
  147. String.prototype.highlight = function (words, highlightTemplate) {
  148. var self = this;
  149. highlightTemplate = highlightTemplate ? highlightTemplate : "<b>{0}</b>";
  150. words.forEach(function (word) {
  151. var searchRegExp = new RegExp("\\b" + word + "\\b", "gi");
  152. self = self.replace(searchRegExp, function (found) {
  153. return highlightTemplate.format(found);
  154. });
  155. });
  156. return self;
  157. };
  158. /**
  159. * Convert time in milliseconds to object contained days, hours and minutes.
  160. * @typedef ConvertedTime
  161. * @type {Object}
  162. * @property {number} d - days
  163. * @property {number} h - hours
  164. * @property {string} m - minutes
  165. * @example
  166. * var time = 1000000000;
  167. * time.toDaysHoursMinutes() // {d: 11, h: 13, m: "46.67"}
  168. *
  169. * @method toDaysHoursMinutes
  170. * @return {object}
  171. */
  172. Number.prototype.toDaysHoursMinutes = function () {
  173. var formatted = {},
  174. dateDiff = this,
  175. secK = 1000, //ms
  176. minK = 60 * secK, // sec
  177. hourK = 60 * minK, // sec
  178. dayK = 24 * hourK;
  179. dateDiff = parseInt(dateDiff);
  180. formatted.d = Math.floor(dateDiff / dayK);
  181. dateDiff -= formatted.d * dayK;
  182. formatted.h = Math.floor(dateDiff / hourK);
  183. dateDiff -= formatted.h * hourK;
  184. formatted.m = (dateDiff / minK).toFixed(2);
  185. return formatted;
  186. };
  187. /**
  188. Sort an array by the key specified in the argument.
  189. Handle only native js objects as element of array, not the Ember's object.
  190. Can be used as alternative to sortProperty method of Ember library
  191. in order to speed up executing on large data volumes
  192. @method sortBy
  193. @param {String} path name(s) to sort on
  194. @return {Array} The sorted array.
  195. */
  196. Array.prototype.sortPropertyLight = function (path) {
  197. var realPath = (typeof path === "string") ? path.split('.') : [];
  198. this.sort(function (a, b) {
  199. var aProperty = a;
  200. var bProperty = b;
  201. realPath.forEach(function (key) {
  202. aProperty = aProperty[key];
  203. bProperty = bProperty[key];
  204. });
  205. if (aProperty > bProperty) return 1;
  206. if (aProperty < bProperty) return -1;
  207. return 0;
  208. });
  209. return this;
  210. };
  211. /** @namespace Em **/
  212. Em.CoreObject.reopen({
  213. t:function (key, attrs) {
  214. return Em.I18n.t(key, attrs)
  215. }
  216. });
  217. Em.TextArea.reopen(Em.I18n.TranslateableAttributes);
  218. /** @namespace Em.Handlebars **/
  219. Em.Handlebars.registerHelper('log', function (variable) {
  220. console.log(variable);
  221. });
  222. Em.Handlebars.registerHelper('warn', function (variable) {
  223. console.warn(variable);
  224. });
  225. Em.Handlebars.registerHelper('highlight', function (property, words, fn) {
  226. var context = (fn.contexts && fn.contexts[0]) || this;
  227. property = Em.Handlebars.getPath(context, property, fn);
  228. words = words.split(";");
  229. // if (highlightTemplate == undefined) {
  230. var highlightTemplate = "<b>{0}</b>";
  231. // }
  232. words.forEach(function (word) {
  233. var searchRegExp = new RegExp("\\b" + word + "\\b", "gi");
  234. property = property.replace(searchRegExp, function (found) {
  235. return highlightTemplate.format(found);
  236. });
  237. });
  238. return new Em.Handlebars.SafeString(property);
  239. });
  240. Em.Handlebars.registerHelper('isAccessible', function (context, options) {
  241. if (App.isAccessible(context)) {
  242. return options.fn(this);
  243. }
  244. });
  245. /**
  246. * @namespace App
  247. */
  248. App = require('app');
  249. /**
  250. * Certain variables can have JSON in string
  251. * format, or in JSON format itself.
  252. *
  253. * @memberof App
  254. * @function parseJson
  255. * @param {string|object}
  256. * @return {object}
  257. */
  258. App.parseJSON = function (value) {
  259. if (typeof value == "string") {
  260. return jQuery.parseJSON(value);
  261. }
  262. return value;
  263. };
  264. /**
  265. * Check for empty <code>Object</code>, built in Em.isEmpty()
  266. * doesn't support <code>Object</code> type
  267. *
  268. * @memberof App
  269. * @method isEmptyObject
  270. * @param obj {Object}
  271. * @return {Boolean}
  272. */
  273. App.isEmptyObject = function(obj) {
  274. var empty = true;
  275. for (var prop in obj) { if (obj.hasOwnProperty(prop)) {empty = false; break;} }
  276. return empty;
  277. }
  278. /**
  279. * Convert object under_score keys to camelCase
  280. *
  281. * @param {Object} object
  282. * @return {Object}
  283. **/
  284. App.keysUnderscoreToCamelCase = function(object) {
  285. var tmp = {};
  286. for (var key in object) {
  287. tmp[stringUtils.underScoreToCamelCase(key)] = object[key];
  288. }
  289. return tmp;
  290. };
  291. /**
  292. * Convert dotted keys to camelcase
  293. *
  294. * @param {Object} object
  295. * @return {Object}
  296. **/
  297. App.keysDottedToCamelCase = function(object) {
  298. var tmp = {};
  299. for (var key in object) {
  300. tmp[key.split('.').reduce(function(p, c) { return p + c.capitalize()})] = object[key];
  301. }
  302. return tmp;
  303. };
  304. /**
  305. * Returns object with defined keys only.
  306. *
  307. * @memberof App
  308. * @method permit
  309. * @param {Object} obj - input object
  310. * @param {String|Array} keys - allowed keys
  311. * @return {Object}
  312. */
  313. App.permit = function(obj, keys) {
  314. var result = {};
  315. if (typeof obj !== 'object' || App.isEmptyObject(obj)) return result;
  316. if (typeof keys == 'string') keys = Array(keys);
  317. keys.forEach(function(key) {
  318. if (obj.hasOwnProperty(key))
  319. result[key] = obj[key];
  320. });
  321. return result;
  322. };
  323. /**
  324. *
  325. * @namespace App
  326. * @namespace App.format
  327. */
  328. App.format = {
  329. /**
  330. * @memberof App.format
  331. * @type {object}
  332. * @property components
  333. */
  334. components: {
  335. 'API': 'API',
  336. 'DECOMMISSION_DATANODE': 'Update Exclude File',
  337. 'DRPC': 'DRPC',
  338. 'FLUME_HANDLER': 'Flume',
  339. 'GLUSTERFS': 'GLUSTERFS',
  340. 'HBASE': 'HBase',
  341. 'HBASE_REGIONSERVER': 'RegionServer',
  342. 'HCAT': 'HCat Client',
  343. 'HDFS': 'HDFS',
  344. 'HISTORYSERVER': 'History Server',
  345. 'HIVE_SERVER': 'HiveServer2',
  346. 'JCE': 'JCE',
  347. 'MAPREDUCE2': 'MapReduce2',
  348. 'MYSQL': 'MySQL',
  349. 'REST': 'REST',
  350. 'SECONDARY_NAMENODE': 'SNameNode',
  351. 'STORM_REST_API': 'Storm REST API Server',
  352. 'WEBHCAT': 'WebHCat',
  353. 'YARN': 'YARN',
  354. 'UI': 'UI',
  355. 'ZKFC': 'ZKFailoverController',
  356. 'ZOOKEEPER': 'ZooKeeper',
  357. 'ZOOKEEPER_QUORUM_SERVICE_CHECK': 'ZK Quorum Service Check'
  358. },
  359. /**
  360. * @memberof App.format
  361. * @property command
  362. * @type {object}
  363. */
  364. command: {
  365. 'INSTALL': 'Install',
  366. 'UNINSTALL': 'Uninstall',
  367. 'START': 'Start',
  368. 'STOP': 'Stop',
  369. 'EXECUTE': 'Execute',
  370. 'ABORT': 'Abort',
  371. 'UPGRADE': 'Upgrade',
  372. 'RESTART': 'Restart',
  373. 'SERVICE_CHECK': 'Check',
  374. 'Excluded:': 'Decommission:',
  375. 'Included:': 'Recommission:'
  376. },
  377. /**
  378. * convert role to readable string
  379. *
  380. * @memberof App.format
  381. * @method role
  382. * @param {string} role
  383. * return {string}
  384. */
  385. role:function (role) {
  386. var result;
  387. var models = [App.StackService, App.StackServiceComponent];
  388. models.forEach(function(model){
  389. var instance = model.find().findProperty('id',role);
  390. if (instance) {
  391. result = instance.get('displayName');
  392. }
  393. },this);
  394. if (!result) {
  395. result = this.normalizeName(role);
  396. }
  397. return result;
  398. },
  399. /**
  400. * Try to format non predefined names to readable format.
  401. *
  402. * @method normalizeName
  403. * @param name {String} - name to format
  404. * @return {String}
  405. */
  406. normalizeName: function(name) {
  407. if (!name || typeof name != 'string') return '';
  408. if (this.components[name]) return this.components[name];
  409. name = name.toLowerCase();
  410. var suffixNoSpaces = ['node','tracker','manager'];
  411. var suffixRegExp = new RegExp('(\\w+)(' + suffixNoSpaces.join('|') + ')', 'gi');
  412. if (/_/g.test(name)) {
  413. name = name.split('_').map(function(singleName) {
  414. return this.normalizeName(singleName.toUpperCase());
  415. }, this).join(' ');
  416. } else if(suffixRegExp.test(name)) {
  417. suffixRegExp.lastIndex = 0;
  418. var matches = suffixRegExp.exec(name);
  419. name = matches[1].capitalize() + matches[2].capitalize();
  420. }
  421. return name.capitalize();
  422. },
  423. /**
  424. * convert command_detail to readable string, show the string for all tasks name
  425. *
  426. * @memberof App.format
  427. * @method commandDetail
  428. * @param {string} command_detail
  429. * @param {string} request_inputs
  430. * @return {string}
  431. */
  432. commandDetail: function (command_detail, request_inputs) {
  433. var detailArr = command_detail.split(' ');
  434. var self = this;
  435. var result = '';
  436. detailArr.forEach( function(item) {
  437. // if the item has the pattern SERVICE/COMPONENT, drop the SERVICE part
  438. if (item.contains('/')) {
  439. item = item.split('/')[1];
  440. }
  441. if (item == 'DECOMMISSION,') {
  442. // ignore text 'DECOMMISSION,'( command came from 'excluded/included'), here get the component name from request_inputs
  443. item = (jQuery.parseJSON(request_inputs)) ? jQuery.parseJSON(request_inputs).slave_type : '';
  444. }
  445. if (self.components[item]) {
  446. result = result + ' ' + self.components[item];
  447. } else if (self.command[item]) {
  448. result = result + ' ' + self.command[item];
  449. } else {
  450. result = result + ' ' + self.role(item);
  451. }
  452. });
  453. if (result.indexOf('Decommission:') > -1 || result.indexOf('Recommission:') > -1) {
  454. // for Decommission command, make sure the hostname is in lower case
  455. result = result.split(':')[0] + ': ' + result.split(':')[1].toLowerCase();
  456. }
  457. if (result === ' Nagios Update Ignore Actionexecute') {
  458. result = Em.I18n.t('common.maintenance.task');
  459. }
  460. if (result.indexOf('Install Packages Actionexecute') != -1) {
  461. result = Em.I18n.t('common.installRepo.task');
  462. }
  463. if (result === ' Rebalancehdfs NameNode') {
  464. result = Em.I18n.t('services.service.actions.run.rebalanceHdfsNodes.title');
  465. }
  466. return result;
  467. },
  468. /**
  469. * Convert uppercase status name to lowercase.
  470. * <br>
  471. * <br>PENDING - Not queued yet for a host
  472. * <br>QUEUED - Queued for a host
  473. * <br>IN_PROGRESS - Host reported it is working
  474. * <br>COMPLETED - Host reported success
  475. * <br>FAILED - Failed
  476. * <br>TIMEDOUT - Host did not respond in time
  477. * <br>ABORTED - Operation was abandoned
  478. *
  479. * @memberof App.format
  480. * @method taskStatus
  481. * @param {string} _taskStatus
  482. * @return {string}
  483. *
  484. */
  485. taskStatus:function (_taskStatus) {
  486. return _taskStatus.toLowerCase();
  487. }
  488. };
  489. /**
  490. * wrapper to bootstrap popover
  491. * fix issue when popover stuck on view routing
  492. *
  493. * @memberof App
  494. * @method popover
  495. * @param {DOMElement} self
  496. * @param {object} options
  497. */
  498. App.popover = function (self, options) {
  499. self.popover(options);
  500. self.on("remove", function () {
  501. $(this).trigger('mouseleave');
  502. });
  503. };
  504. /**
  505. * wrapper to bootstrap tooltip
  506. * fix issue when tooltip stuck on view routing
  507. * @memberof App
  508. * @method tooltip
  509. * @param {DOMElement} self
  510. * @param {object} options
  511. */
  512. App.tooltip = function (self, options) {
  513. self.tooltip(options);
  514. /* istanbul ignore next */
  515. self.on("remove", function () {
  516. $(this).trigger('mouseleave');
  517. });
  518. };
  519. /**
  520. * wrapper to Date().getTime()
  521. * fix issue when client clock and server clock not sync
  522. *
  523. * @memberof App
  524. * @method dateTime
  525. * @return {Number} timeStamp of current server clock
  526. */
  527. App.dateTime = function() {
  528. return new Date().getTime() + App.clockDistance;
  529. };
  530. /**
  531. * Helper function for bound property helper registration
  532. * @memberof App
  533. * @method registerBoundHelper
  534. * @param name {String} name of helper
  535. * @param view {Em.View} view
  536. */
  537. App.registerBoundHelper = function(name, view) {
  538. Em.Handlebars.registerHelper(name, function(property, options) {
  539. options.hash.contentBinding = property;
  540. return Em.Handlebars.helpers.view.call(this, view, options);
  541. });
  542. };
  543. /*
  544. * Return singular or plural word based on Em.I18n, view|controller context property key.
  545. *
  546. * Example: {{pluralize hostsCount singular="t:host" plural="t:hosts"}}
  547. * {{pluralize hostsCount singular="@view.hostName"}}
  548. */
  549. App.registerBoundHelper('pluralize', Em.View.extend({
  550. tagName: 'span',
  551. template: Em.Handlebars.compile('{{view.wordOut}}'),
  552. wordOut: function() {
  553. var count, singular, plural;
  554. count = this.get('content');
  555. singular = this.get('singular');
  556. plural = this.get('plural');
  557. return this.getWord(count, singular, plural);
  558. }.property('content'),
  559. /**
  560. * Get computed word.
  561. *
  562. * @param {Number} count
  563. * @param {String} singular
  564. * @param {String} [plural]
  565. * @return {String}
  566. * @method getWord
  567. */
  568. getWord: function(count, singular, plural) {
  569. singular = this.parseValue(singular);
  570. // if plural not passed
  571. if (!plural) plural = singular + 's';
  572. else plural = this.parseValue(plural);
  573. if (singular && plural) {
  574. if (count == 1) {
  575. return singular;
  576. } else {
  577. return plural;
  578. }
  579. }
  580. return '';
  581. },
  582. /**
  583. * Detect and return value from its instance.
  584. *
  585. * @param {String} value
  586. * @return {*}
  587. * @method parseValue
  588. **/
  589. parseValue: function(value) {
  590. switch (value[0]) {
  591. case '@':
  592. value = this.getViewPropertyValue(value);
  593. break;
  594. case 't':
  595. value = this.tDetect(value);
  596. break;
  597. default:
  598. break;
  599. }
  600. return value;
  601. },
  602. /*
  603. * Detect for Em.I18n.t reference call
  604. * @params word {String}
  605. * return {String}
  606. */
  607. tDetect: function(word) {
  608. var splitted = word.split(':');
  609. if (splitted.length > 1 && splitted[0] == 't') {
  610. return Em.I18n.t(splitted[1]);
  611. } else {
  612. return splitted[0];
  613. }
  614. },
  615. /**
  616. * Get property value from view|controller by its key path.
  617. *
  618. * @param {String} value - key path
  619. * @return {*}
  620. * @method getViewPropertyValue
  621. **/
  622. getViewPropertyValue: function(value) {
  623. value = value.substr(1);
  624. var keyword = value.split('.')[0]; // return 'controller' or 'view'
  625. switch (keyword) {
  626. case 'controller':
  627. return Em.get(this, value);
  628. case 'view':
  629. return Em.get(this, value.replace(/^view/, 'parentView'))
  630. default:
  631. break;
  632. }
  633. }
  634. })
  635. );
  636. /**
  637. * Return defined string instead of empty if value is null/undefined
  638. * by default is `n/a`.
  639. *
  640. * @param empty {String} - value instead of empty string (not required)
  641. * can be used with Em.I18n pass value started with't:'
  642. *
  643. * Examples:
  644. *
  645. * default value will be returned
  646. * {{formatNull service.someValue}}
  647. *
  648. * <code>empty<code> will be returned
  649. * {{formatNull service.someValue empty="I'm empty"}}
  650. *
  651. * Em.I18n translation will be returned
  652. * {{formatNull service.someValue empty="t:my.key.to.translate"
  653. */
  654. App.registerBoundHelper('formatNull', Em.View.extend({
  655. tagName: 'span',
  656. template: Em.Handlebars.compile('{{view.result}}'),
  657. result: function() {
  658. var emptyValue = this.get('empty') ? this.get('empty') : Em.I18n.t('services.service.summary.notAvailable');
  659. emptyValue = emptyValue.startsWith('t:') ? Em.I18n.t(emptyValue.substr(2, emptyValue.length)) : emptyValue;
  660. return (this.get('content') || this.get('content') == 0) ? this.get('content') : emptyValue;
  661. }.property('content')
  662. }));
  663. /**
  664. * Return formatted string with inserted <code>wbr</code>-tag after each dot
  665. *
  666. * @param {String} content
  667. *
  668. * Examples:
  669. *
  670. * returns 'apple'
  671. * {{formatWordBreak 'apple'}}
  672. *
  673. * returns 'apple.<wbr />banana'
  674. * {{formatWordBreak 'apple.banana'}}
  675. *
  676. * returns 'apple.<wbr />banana.<wbr />uranium'
  677. * {{formatWordBreak 'apple.banana.uranium'}}
  678. */
  679. App.registerBoundHelper('formatWordBreak', Em.View.extend({
  680. tagName: 'span',
  681. template: Em.Handlebars.compile('{{{view.result}}}'),
  682. /**
  683. * @type {string}
  684. */
  685. result: function() {
  686. return this.get('content') && this.get('content').replace(/\./g, '.<wbr />');
  687. }.property('content')
  688. }));
  689. /**
  690. * Return <i></i> with class that correspond to status
  691. *
  692. * @param {string} content - status
  693. *
  694. * Examples:
  695. *
  696. * {{statusIcon view.status}}
  697. * returns 'icon-cog'
  698. *
  699. */
  700. App.registerBoundHelper('statusIcon', Em.View.extend({
  701. tagName: 'i',
  702. /**
  703. * relation map between status and icon class
  704. * @type {object}
  705. */
  706. statusIconMap: {
  707. 'COMPLETED': 'icon-ok completed',
  708. 'WARNING': 'icon-warning-sign',
  709. 'FAILED': 'icon-exclamation-sign failed',
  710. 'HOLDING_FAILED': 'icon-exclamation-sign failed',
  711. 'PENDING': 'icon-cog pending',
  712. 'QUEUED': 'icon-cog queued',
  713. 'IN_PROGRESS': 'icon-cogs in_progress',
  714. 'HOLDING': 'icon-pause',
  715. 'ABORTED': 'icon-minus aborted',
  716. 'TIMEDOUT': 'icon-time timedout',
  717. 'HOLDING_TIMEDOUT': 'icon-time timedout'
  718. },
  719. classNameBindings: ['iconClass'],
  720. /**
  721. * @type {string}
  722. */
  723. iconClass: function () {
  724. return this.get('statusIconMap')[this.get('content')] || 'icon-question-sign';
  725. }.property('content')
  726. }));
  727. /**
  728. * Ambari overrides the default date transformer.
  729. * This is done because of the non-standard data
  730. * sent. For example Nagios sends date as "12345678".
  731. * The problem is that it is a String and is represented
  732. * only in seconds whereas Javascript's Date needs
  733. * milliseconds representation.
  734. */
  735. DS.attr.transforms.date = {
  736. from: function (serialized) {
  737. var type = typeof serialized;
  738. if (type === Em.I18n.t('common.type.string')) {
  739. serialized = parseInt(serialized);
  740. type = typeof serialized;
  741. }
  742. if (type === Em.I18n.t('common.type.number')) {
  743. if (!serialized ){ //serialized timestamp = 0;
  744. return 0;
  745. }
  746. // The number could be seconds or milliseconds.
  747. // If seconds, then the length is 10
  748. // If milliseconds, the length is 13
  749. if (serialized.toString().length < 13) {
  750. serialized = serialized * 1000;
  751. }
  752. return new Date(serialized);
  753. } else if (serialized === null || serialized === undefined) {
  754. // if the value is not present in the data,
  755. // return undefined, not null.
  756. return serialized;
  757. } else {
  758. return null;
  759. }
  760. },
  761. to: function (deserialized) {
  762. if (deserialized instanceof Date) {
  763. return deserialized.getTime();
  764. } else if (deserialized === undefined) {
  765. return undefined;
  766. } else {
  767. return null;
  768. }
  769. }
  770. };
  771. DS.attr.transforms.object = {
  772. from: function(serialized) {
  773. return Ember.none(serialized) ? null : Object(serialized);
  774. },
  775. to: function(deserialized) {
  776. return Ember.none(deserialized) ? null : Object(deserialized);
  777. }
  778. };
  779. /**
  780. * Allows EmberData models to have array properties.
  781. *
  782. * Declare the property as <code>
  783. * operations: DS.attr('array'),
  784. * </code> and
  785. * during load provide a JSON array for value.
  786. *
  787. * This transform simply assigns the same array in both directions.
  788. */
  789. DS.attr.transforms.array = {
  790. from : function(serialized) {
  791. return serialized;
  792. },
  793. to : function(deserialized) {
  794. return deserialized;
  795. }
  796. };
  797. /**
  798. * Utility method to delete all existing records of a DS.Model type from the model's associated map and
  799. * store's persistence layer (recordCache)
  800. * @param type DS.Model Class
  801. */
  802. App.resetDsStoreTypeMap = function(type) {
  803. var allRecords = App.get('store.recordCache'); //This fetches all records in the ember-data persistence layer
  804. var typeMaps = App.get('store.typeMaps');
  805. var guidForType = Em.guidFor(type);
  806. var typeMap = typeMaps[guidForType];
  807. if (typeMap) {
  808. var idToClientIdMap = typeMap.idToCid;
  809. for (var id in idToClientIdMap) {
  810. if (idToClientIdMap.hasOwnProperty(id) && idToClientIdMap[id]) {
  811. delete allRecords[idToClientIdMap[id]]; // deletes the cached copy of the record from the store
  812. }
  813. }
  814. typeMaps[guidForType] = {
  815. idToCid: {},
  816. clientIds: [],
  817. cidToHash: {},
  818. recordArrays: []
  819. };
  820. }
  821. };