utils.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. var multipleClustersSupported = false;
  2. /* Super-handy augmentation to Function that allows 'this' to be bound
  3. * statically.
  4. *
  5. * Primarily used when creating objects whose methods will be used as
  6. * callbacks in unknown contexts.
  7. *
  8. * Look at txnUtils.js for sample usage.
  9. */
  10. Function.prototype.bind = function(scope) {
  11. var _function = this;
  12. return function() {
  13. return _function.apply(scope, arguments);
  14. }
  15. }
  16. /* Belongs with createInformationalPanel() */
  17. var customizedYuiPanelCss = false;
  18. /* XXX Ugly, ugly hack to emulate a singleton - users are NEVER supposed
  19. * to use this, and should only access the createInformationalPanel() and
  20. * destroyInformationalPanel() methods below.
  21. */
  22. var globalSingletonInformationalPanel;
  23. function createInformationalPanel( containerNodeId, headerContentString ) {
  24. /* XXX This should check that globalSingletonInformationalPanel is within
  25. * containerNodeId, and only then perform this cleanup, but this whole
  26. * panel-related section needs to be rewritten anyway - for now, we only
  27. * support the one globalSingletonInformationalPanel, and passing in
  28. * anything other than #informationalPanelContainerDivId as containerNodeId
  29. * is not guaranteed to work.
  30. */
  31. if( globalSingletonInformationalPanel ) {
  32. destroyInformationalPanel( globalSingletonInformationalPanel );
  33. }
  34. globalYui.one( containerNodeId ).append('<div id="informationalPanelInnerContainerDivId"></div>');
  35. var newPanel = new globalYui.Panel({
  36. srcNode: '#informationalPanelInnerContainerDivId',
  37. headerContent: headerContentString,
  38. width: 800,
  39. height: 400,
  40. render: true,
  41. modal: true,
  42. zIndex: 100,
  43. visible: false
  44. });
  45. if( !customizedYuiPanelCss ) {
  46. /* Needs to be called one time only.
  47. *
  48. * We do this here instead of creating a static entry in a CSS file
  49. * because the first invocation of globalYui.Panel (above) pulls in all
  50. * the necessary additional styling information that is applied to the
  51. * panel - since this new styling information comes in at runtime, it
  52. * overrides any static CSS we might have had, so adding our overrides
  53. * at runtime (*after* globalYui.Panel) is the only way out.
  54. */
  55. /*
  56. globalYui.StyleSheet('KewlApp').set( '.yui3-skin-sam .yui3-panel-content .yui3-widget-hd', {
  57. background: 'rgb(50,185,50)',
  58. textAlign: 'center',
  59. fontWeight: 'bold',
  60. fontSize: '150%'
  61. });
  62. globalYui.StyleSheet('KewlApp').set( '.yui3-skin-sam .yui3-panel .yui3-widget-hd .yui3-button-close', {
  63. border: 'medium solid gray',
  64. backgroundColor: 'white',
  65. height: '17px',
  66. width: '20px',
  67. });
  68. */
  69. // Temporary fix to a yui bug
  70. globalYui.StyleSheet('KewlApp').set( '.yui3-skin-sam .yui3-panel .yui3-widget-hd .yui3-button-close:before', {
  71. content: 'url(../yui-3.5.1/build/assets/skins/sam/sprite_icons.png)',
  72. backgroundImage: 'url(../yui-3.5.1/build/assets/skins/sam/sprite_icons.png)',
  73. });
  74. /*
  75. globalYui.StyleSheet('KewlApp').set( '.yui3-skin-sam .yui3-panel-content .yui3-widget-ft', {
  76. background: 'rgb(50,185,50)',
  77. });
  78. globalYui.StyleSheet('KewlApp').set( '.yui3-skin-sam .yui3-panel-content .yui3-widget-ft .yui3-button', {
  79. fontWeight: 'bold',
  80. fontSize: '135%',
  81. color: 'white',
  82. margin: '0pt 6px',
  83. padding: '4px 8px',
  84. textDecoration: 'underline',
  85. background: 'none',
  86. border: '0px'
  87. });
  88. */
  89. customizedYuiPanelCss = true;
  90. }
  91. globalSingletonInformationalPanel = newPanel;
  92. return newPanel;
  93. }
  94. function destroyInformationalPanel( theInformationalPanelInstance ) {
  95. if( theInformationalPanelInstance ) {
  96. theInformationalPanelInstance.hide();
  97. theInformationalPanelInstance.destroy();
  98. if( theInformationalPanelInstance === globalSingletonInformationalPanel ) {
  99. globalSingletonInformationalPanel = null;
  100. }
  101. }
  102. }
  103. function showLoadingImg() {
  104. globalYui.one("#loadingDivId").setStyle('display','block');
  105. //globalYui.one("#blackScreenDivId").setStyle('display','block');
  106. //globalYui.one("#loadingImgId").setStyle('display','block');
  107. }
  108. function hideLoadingImg() {
  109. globalYui.one("#loadingDivId").setStyle('display','none');
  110. //globalYui.one("#loadingImgId").setStyle('display','none');
  111. //globalYui.one("#blackScreenDivId").setStyle('display','none');
  112. }
  113. function swapStageVisibilities( currentStageDivSelector, newStageDivSelector ) {
  114. globalYui.log("In swapStageVisibilities: " + currentStageDivSelector + "->" + newStageDivSelector);
  115. /* Hide the current stage. */
  116. globalYui.one(currentStageDivSelector).setStyle('display','none');
  117. /* Show the new stage. */
  118. globalYui.one(newStageDivSelector).setStyle('display','block');
  119. }
  120. /* TODO XXX Consider bundling the last 3 parameters into their own NewStage object.
  121. * TODO XXX Do the same for the first 2 parameters and a CurrentStage object.
  122. */
  123. function transitionToNextStage( currentStageDivSelector, currentStageData, newStageDivSelector, newStageData, newStageRenderFunction ) {
  124. clearFormStatus();
  125. globalYui.one(currentStageDivSelector).setStyle('display','none');
  126. /* Render the next stage. */
  127. newStageRenderFunction(newStageData);
  128. globalYui.log("In transitionToNextStage: " + currentStageDivSelector + "->" + newStageDivSelector);
  129. //// tshooter: No longer doing this given dynamic rendering on stages. Only hide current stage.
  130. /* And make it visibly replace the currently showing one. */
  131. ///// tshooter: commented: swapStageVisibilities(currentStageDivSelector, newStageDivSelector);
  132. /* And now, handle the updates to addNodesWizardStages... */
  133. /* There can be only one 'current' stage at a time. */
  134. var currentStage = globalYui.one('.installationWizardCurrentStage');
  135. if ( currentStage ) {
  136. var nextStage = null;
  137. /* Check to make sure we haven't reached the last stage. */
  138. if( nextStage = currentStage.next('.installationWizardUnvisitedStage') ) {
  139. /* Mark this up-until-now 'current' stage as 'visited'. */
  140. currentStage.replaceClass( 'installationWizardCurrentStage', 'installationWizardVisitedStage' );
  141. /* Mark the stage after that as the new 'current' stage. */
  142. nextStage.replaceClass( 'installationWizardUnvisitedStage', 'installationWizardCurrentStage' );
  143. }
  144. }
  145. }
  146. function clearFormStatus() {
  147. var formStatusDiv = globalYui.all("#formStatusDivId");
  148. // formStatusDiv.setContent("");
  149. formStatusDiv.setStyle("visibility", "hidden");
  150. formStatusDiv.setStyle("display", "none");
  151. formStatusDiv.set('className','');
  152. formStatusDiv.addClass("formStatusBar");
  153. }
  154. function setFormStatus(statusString, isError, noFade) {
  155. var formStatusDivCssClass;
  156. if (isError) {
  157. formStatusDivCssClass = 'statusError';
  158. } else {
  159. formStatusDivCssClass = 'statusOk';
  160. }
  161. var formStatusDiv = globalYui.all("#formStatusDivId");
  162. formStatusDiv.setStyle("visibility", "visible");
  163. formStatusDiv.setStyle("display", "block");
  164. formStatusDiv.set('className','');
  165. formStatusDiv.addClass("formStatusBar");
  166. formStatusDiv.addClass(formStatusDivCssClass);
  167. formStatusDiv.setContent(statusString);
  168. if (!isError && !noFade) {
  169. //setTimeout(fadeFormStatus, 1000);
  170. }
  171. }
  172. function fadeFormStatus() {
  173. var formStatusDiv = globalYui.one("#formStatusDivId");
  174. formStatusDiv.addClass("formStatusBarZeroOpacity");
  175. }
  176. function convertDisplayType (displayType) {
  177. switch (displayType) {
  178. case "NODISPLAY":
  179. return "NODISPLAY";
  180. case "TEXT":
  181. return "text";
  182. case "SECRET":
  183. return "password";
  184. case "ONOFF":
  185. return "checkbox";
  186. default:
  187. return "text";
  188. }
  189. }
  190. function executeStage(inputUrl, renderStageFunction) {
  191. globalYui.io(inputUrl, {
  192. method: 'GET',
  193. timeout : 10000,
  194. on: {
  195. success: function (x,o) {
  196. globalYui.log("RAW JSON DATA: " + o.responseText);
  197. // Process the JSON data returned from the server
  198. try {
  199. responseJson = globalYui.JSON.parse(o.responseText);
  200. }
  201. catch (e) {
  202. hideLoadingImg();
  203. alert("JSON Parse failed!");
  204. return;
  205. }
  206. globalYui.log("PARSED DATA: " + globalYui.Lang.dump(responseJson));
  207. if (responseJson.result != 0) {
  208. hideLoadingImg();
  209. // Error!
  210. alert("Got error during getting data: " + responseJson.error);
  211. return;
  212. }
  213. responseJson = responseJson.response;
  214. renderStageFunction(responseJson);
  215. hideLoadingImg();
  216. return;
  217. },
  218. failure: function (x,o) {
  219. alert("Async call failed!");
  220. return;
  221. }
  222. }
  223. });
  224. }
  225. function submitDataAndProgressToNextScreen(url, requestData, submitButton, thisScreenId, nextScreenId, nextScreenRenderFunction, errorHandlerFunction) {
  226. showLoadingImg();
  227. globalYui.io(url, {
  228. method: 'POST',
  229. data: globalYui.JSON.stringify(requestData),
  230. timeout : 10000,
  231. on: {
  232. start: function(x, o) {
  233. submitButton.set('disabled', true);
  234. globalYui.log("In start function");
  235. // showLoadingImg();
  236. },
  237. complete: function(x, o) {
  238. submitButton.set('disabled', false);
  239. globalYui.log("In stop function");
  240. // hideLoadingImg();
  241. },
  242. success: function (x,o) {
  243. submitButton.set('disabled', false);
  244. globalYui.log("RAW JSON DATA: " + o.responseText);
  245. // Process the JSON data returned from the server
  246. try {
  247. responseJson = globalYui.JSON.parse(o.responseText);
  248. }
  249. catch (e) {
  250. submitButton.set('disabled', false);
  251. hideLoadingImg();
  252. alert("JSON Parse failed!");
  253. return;
  254. }
  255. globalYui.log("PARSED DATA: " + globalYui.Lang.dump(responseJson));
  256. if (responseJson.result != 0) {
  257. submitButton.set('disabled', false);
  258. // Error!
  259. globalYui.log("Got error during submit data!" + responseJson.error);
  260. if ( errorHandlerFunction ) {
  261. globalYui.log("Invoking error handler function");
  262. errorHandlerFunction(responseJson);
  263. } else {
  264. alert("Got error during submit data!" + responseJson.error);
  265. }
  266. hideLoadingImg();
  267. return;
  268. }
  269. responseJson = responseJson.response;
  270. /* Done with this stage, transition to the next. */
  271. transitionToNextStage( thisScreenId, requestData, nextScreenId, responseJson, nextScreenRenderFunction );
  272. },
  273. failure: function (x,o) {
  274. submitButton.set('disabled', false);
  275. alert("Async call failed!");
  276. }
  277. }
  278. });
  279. }
  280. function PeriodicDataPoller( dataSourceContext, responseHandler ) {
  281. this.dataSourceContext = dataSourceContext;
  282. /* Smoothe out the optional bits of this.dataSourceContext. */
  283. if( !this.dataSourceContext.pollInterval ) {
  284. /* How often we poll. */
  285. this.dataSourceContext.pollInterval = 5000;
  286. }
  287. if( !this.dataSourceContext.maxFailedAttempts ) {
  288. /* How many failed attempts before we stop polling. */
  289. this.dataSourceContext.maxFailedAttempts = 5;
  290. }
  291. this.responseHandler = responseHandler;
  292. /* Of course, we're not paused when we start off. */
  293. this.paused = false;
  294. this.dataSource = new globalYui.DataSource.IO ({
  295. source: this.dataSourceContext.source
  296. });
  297. this.dataSource.plug(globalYui.Plugin.DataSourceJSONSchema, {
  298. schema: this.dataSourceContext.schema
  299. });
  300. this.dataSourcePollFailureCount = 0;
  301. /* Set when start() is invoked. */
  302. this.dataSourcePollHandle = null;
  303. this.dataSourcePollRequestContext = {
  304. request: this.dataSourceContext.request,
  305. callback: {
  306. success: function (e) {
  307. /* Avoid race conditions in JS by not processing incoming responses
  308. * from the backend if the PDP is paused (which is our signal that
  309. * a previous response is still in the middle of being processed).
  310. */
  311. if( !(this.isPaused()) ) {
  312. /* Reset our failure count every time we succeed. */
  313. this.dataSourcePollFailureCount = 0;
  314. /* Invoke user-pluggable code. */
  315. if( this.responseHandler.success ) {
  316. this.responseHandler.success( e, this );
  317. }
  318. }
  319. }.bind(this),
  320. failure: function (e) {
  321. if( !(this.isPaused()) ) {
  322. ++this.dataSourcePollFailureCount;
  323. if( this.dataSourcePollFailureCount > this.dataSourceContext.maxFailedAttempts ) {
  324. /* Invoke user-pluggable code. */
  325. if( this.responseHandler.failure ) {
  326. this.responseHandler.failure( e, this );
  327. }
  328. /* No point making any more attempts. */
  329. this.stop();
  330. }
  331. }
  332. }.bind(this)
  333. }
  334. };
  335. }
  336. /* Start polling. */
  337. PeriodicDataPoller.prototype.start = function() {
  338. this.dataSourcePollHandle = this.dataSource.setInterval
  339. ( this.dataSourceContext.pollInterval, this.dataSourcePollRequestContext );
  340. }
  341. /* Stop polling. */
  342. PeriodicDataPoller.prototype.stop = function() {
  343. /* Always unPause() during stop(), so the next start() won't be neutered. */
  344. this.unPause();
  345. this.dataSource.clearInterval( this.dataSourcePollHandle );
  346. }
  347. /* When the PDP is paused, the polling continues on its regular fixed
  348. * interval, but this.responseHandler is not invoked, thus avoiding
  349. * a race condition (at least) in JS.
  350. *
  351. * TODO XXX Improve upon this to not even make calls to the backend
  352. * while not losing our periodicity.
  353. */
  354. PeriodicDataPoller.prototype.pause = function() {
  355. this.paused = true;
  356. }
  357. PeriodicDataPoller.prototype.unPause = function() {
  358. this.paused = false;
  359. }
  360. PeriodicDataPoller.prototype.isPaused = function() {
  361. return this.paused;
  362. }
  363. /* Perform a one-time poll.
  364. *
  365. * Meant to be used when the polling is not at a set frequency (as with the
  366. * start()/stop() pair), and is instead meant to be under explicit
  367. * control of the application.
  368. */
  369. PeriodicDataPoller.prototype.pollOnce = function() {
  370. globalYui.io(this.dataSourceContext.source + this.dataSourcePollRequestContext.request, {
  371. on: this.dataSourcePollRequestContext.callback
  372. });
  373. }
  374. function titleCase(word){
  375. return word.substr(0,1).toUpperCase() + word.substr(1).toLowerCase();
  376. }