utils.js 14 KB

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