/zk/src/archive/web/js/zk/au.js

http://github.com/zkoss/zk · JavaScript · 1956 lines · 1253 code · 98 blank · 605 comment · 416 complexity · c86bc55be7599c6a047fa19b4c294a08 MD5 · raw file

Large files are truncated click here to view the full file

  1. /* au.js
  2. Purpose:
  3. ZK Client Engine
  4. Description:
  5. History:
  6. Mon Sep 29 17:17:37 2008, Created by tomyeh
  7. Copyright (C) 2008 Potix Corporation. All Rights Reserved.
  8. This program is distributed under LGPL Version 2.1 in the hope that
  9. it will be useful, but WITHOUT ANY WARRANTY.
  10. */
  11. (function () {
  12. var _perrURIs = {}, //server-push error URI
  13. _onErrs = [], //onError functions
  14. cmdsQue = [], //response commands in XML
  15. sendPending, ctlUuid, ctlTime, ctlCmd, responseId,
  16. doCmdFns = [],
  17. idTimeout, //timer ID for automatica timeout
  18. pfIndex = 0, //performance meter index
  19. _detached = [], //used for resolving #stub/#stubs in mount.js (it stores detached widgets in this AU)
  20. Widget = zk.Widget,
  21. _portrait = {'0': true, '180': true}, //default portrait definition
  22. _initLandscape = jq.innerWidth() > jq.innerHeight(), // initial orientation is landscape or not
  23. _initDefault = _portrait[window.orientation], //default orientation
  24. _aftAuResp = []; //store callbacks to be triggered when au is back
  25. // Checks whether to turn off the progress prompt
  26. function checkProgressing(sid) {
  27. if (!zAu.processing()) {
  28. _detached = []; //clean up
  29. if (!zk.clientinfo)
  30. zk.endProcessing(sid);
  31. //setTimeout(zk.endProcessing, 50);
  32. // using a timeout to stop the processing after doing onSize in the fireSized() method of the Utl.js
  33. //Bug ZK-1505: using timeout cause progress bar disapper such as Thread.sleep(1000) case, so revert it back
  34. zAu.doneTime = jq.now();
  35. }
  36. }
  37. function pushCmds(cmds, rs) {
  38. for (var j = 0, rl = rs ? rs.length : 0; j < rl; ++j) {
  39. var r = rs[j],
  40. cmd = r[0],
  41. data = r[1];
  42. if (!cmd) {
  43. zAu.showError('ILLEGAL_RESPONSE', 'command required');
  44. continue;
  45. }
  46. cmds.push({cmd: cmd, data: data || []});
  47. }
  48. cmdsQue.push(cmds);
  49. }
  50. function dataNotReady(cmd, data) {
  51. for (var j = data.length, id, w; j--;)
  52. if (id = data[j] && data[j].$u) {
  53. if (!(w = Widget.$(id))) { //not ready
  54. zk.afterMount(function () {
  55. do {
  56. if (id = data[j] && data[j].$u)
  57. data[j] = Widget.$(id);
  58. } while (j--);
  59. doProcess(cmd, data);
  60. }, -1);
  61. return true; //not ready
  62. }
  63. data[j] = w;
  64. }
  65. }
  66. function doProcess(cmd, data) { //decoded
  67. if (!dataNotReady(cmd, data)) {
  68. if (!zAu.processPhase) {
  69. zAu.processPhase = cmd;
  70. }
  71. //1. process zAu.cmd1 (cmd1 has higher priority)
  72. var fn = zAu.cmd1[cmd];
  73. if (fn) {
  74. if (!data.length)
  75. return zAu.showError('ILLEGAL_RESPONSE', 'uuid required', cmd);
  76. data[0] = Widget.$(data[0]); //might be null (such as rm)
  77. // Bug ZK-2827
  78. if (!data[0] && cmd != 'invoke' && cmd != 'addChd' /*Bug ZK-2839*/) {
  79. return;
  80. }
  81. if (cmd == 'setAttr' || cmd == 'setAttrs') {
  82. if (!zAu.doAfterProcessWgts) {
  83. zAu.doAfterProcessWgts = [];
  84. }
  85. zAu.doAfterProcessWgts.push(data[0]);
  86. }
  87. } else {
  88. //2. process zAu.cmd0
  89. fn = zAu.cmd0[cmd];
  90. if (!fn)
  91. return zAu.showError('ILLEGAL_RESPONSE', 'Unknown', cmd);
  92. }
  93. fn.apply(zAu, data);
  94. zAu.processPhase = null;
  95. }
  96. }
  97. function ajaxReqResend2() {
  98. var reqInf = zAu.pendingReqInf;
  99. if (reqInf) {
  100. zAu.pendingReqInf = null;
  101. if (zAu.seqId == reqInf.sid)
  102. ajaxSendNow(reqInf);
  103. }
  104. }
  105. function _exmsg(e) {
  106. var msg = e.message || e, m2 = '';
  107. if (e.name) m2 = ' ' + e.name;
  108. // if (e.fileName) m2 += " " +e.fileName;
  109. // if (e.lineNumber) m2 += ":" +e.lineNumber;
  110. // if (e.stack) m2 += " " +e.stack;
  111. return msg + (m2 ? ' (' + m2.substring(1) + ')' : m2);
  112. }
  113. function ajaxSend(dt, aureq, timeout) {
  114. //ZK-1523: dt(desktop) could be null, so search the desktop from target's parent.
  115. //call stack: echo2() -> send()
  116. if (!dt) {
  117. //original dt is decided by aureq.target.desktop, so start by it's parent.
  118. var wgt = aureq.target.parent;
  119. while (!wgt.desktop) {
  120. wgt = wgt.parent;
  121. }
  122. dt = wgt.desktop;
  123. }
  124. ////
  125. zAu.addAuRequest(dt, aureq);
  126. ajaxSend2(dt, timeout);
  127. //Note: we don't send immediately (Bug 1593674)
  128. }
  129. function ajaxSend2(dt, timeout) {
  130. if (!timeout) timeout = 0;
  131. if (dt && timeout >= 0)
  132. setTimeout(function () {zAu.sendNow(dt);}, timeout);
  133. }
  134. function ajaxSendNow(reqInf) {
  135. var setting = zAu.ajaxSettings,
  136. req = setting.xhr();
  137. zAu.sentTime = jq.now(); //used by server-push (cpsp)
  138. try {
  139. zk.ausending = true;
  140. if (zk.xhrWithCredentials)
  141. req.withCredentials = true;
  142. req.onreadystatechange = zAu._onResponseReady;
  143. req.open('POST', reqInf.uri, true);
  144. req.setRequestHeader('Content-Type', setting.contentType);
  145. req.setRequestHeader('ZK-SID', reqInf.sid);
  146. if (zAu._errCode) {
  147. req.setRequestHeader('ZK-Error-Report', zAu._errCode);
  148. zAu._errCode = null;
  149. }
  150. var forceAjax = reqInf.forceAjax;
  151. if (zk.pfmeter) zAu._pfsend(reqInf.dt, req, false, forceAjax);
  152. zAu.ajaxReq = req;
  153. zAu.ajaxReqInf = reqInf;
  154. if (!forceAjax && typeof zWs != 'undefined' && zWs.ready) {
  155. zWs.send(req, reqInf);
  156. return;
  157. }
  158. req.send(reqInf.content);
  159. if (!reqInf.implicit)
  160. zk.startProcessing(zk.procDelay, reqInf.sid); //wait a moment to avoid annoying
  161. } catch (e) {
  162. //handle error
  163. try {
  164. if (typeof req.abort == 'function') req.abort();
  165. } catch (e2) {
  166. zk.debugLog(e2.message || e2);
  167. }
  168. if (!reqInf.ignorable && !zk.unloading) {
  169. var msg = _exmsg(e);
  170. zAu._errCode = '[Send] ' + msg;
  171. if (zAu.confirmRetry('FAILED_TO_SEND', msg)) {
  172. ajaxReqResend(reqInf);
  173. return;
  174. }
  175. }
  176. }
  177. }
  178. function doCmdsNow(cmds) {
  179. var rtags = cmds.rtags || {}, ex;
  180. try {
  181. while (cmds && cmds.length) {
  182. if (zk.mounting) return false;
  183. var cmd = cmds.shift();
  184. try {
  185. doProcess(cmd.cmd, cmd.data);
  186. } catch (e) {
  187. zk.mounting = false; //make it able to proceed
  188. zAu.showError('FAILED_TO_PROCESS', null, cmd.cmd, e);
  189. if (!ex) ex = e;
  190. }
  191. }
  192. if (zAu.doAfterProcessWgts) {
  193. zAu.doAfterProcessWgts.forEach(function (wgt) {
  194. if (wgt.doAfterProcessRerenderArgs) {
  195. wgt.rerender.apply(wgt, wgt.doAfterProcessRerenderArgs);
  196. wgt.doAfterProcessRerenderArgs = null;
  197. }
  198. });
  199. zAu.doAfterProcessWgts = null;
  200. }
  201. } finally {
  202. //Bug #2871135, always fire since the client might send back empty
  203. if (!cmds || !cmds.length) {
  204. // ZK-3288, If the wpd file of new created widget was never loaded,
  205. // sometimes onCommandReady and onResponse will be called during the widget mounting phase. (timing issue)
  206. zk.afterMount(function () {
  207. // Bug ZK-2516
  208. zWatch.fire('onCommandReady', null, {timeout: -1, rtags: rtags}); //won't use setTimeout
  209. zWatch.fire('onResponse', null, {timeout: 0, rtags: rtags}); //use setTimeout
  210. if (rtags.onClientInfo) {
  211. setTimeout(zk.endProcessing, 50); // always stop the processing
  212. delete zk.clientinfo;
  213. }
  214. }, -1);
  215. }
  216. zk.ausending = false;
  217. zk.doAfterAuResponse();
  218. }
  219. if (ex)
  220. throw ex;
  221. return true;
  222. }
  223. function _asBodyChild(child) {
  224. jq(document.body).append(child);
  225. }
  226. //misc//
  227. function fireClientInfo() {
  228. zAu.cmd0.clientInfo();
  229. }
  230. function sendTimeout() {
  231. zAu.send(new zk.Event(null, 'dummy', null, {ignorable: true, serverAlive: true, rtags: {isDummy: true}, forceAjax: true}));
  232. //serverAlive: the server shall not ignore it if session timeout
  233. zk.isTimeout = true; //ZK-3304: already timeout
  234. }
  235. //store all widgets into a map
  236. function _wgt2map(wgt, map) {
  237. map[wgt.uuid] = wgt;
  238. for (wgt = wgt.firstChild; wgt; wgt = wgt.nextSibling)
  239. _wgt2map(wgt, map);
  240. }
  241. function _beforeAction(wgt, actnm) {
  242. var act;
  243. if (wgt._visible && (act = wgt.actions_[actnm])) {
  244. wgt.z$display = 'none'; //control zk.Widget.domAttrs_
  245. return act;
  246. }
  247. }
  248. function _afterAction(wgt, act) {
  249. if (act) {
  250. delete wgt.z$display;
  251. act[0].call(wgt, wgt.$n(), act[1]);
  252. return true;
  253. }
  254. }
  255. /** @class zAu
  256. * @import zk.Widget
  257. * @import zk.Desktop
  258. * @import zk.Event
  259. * @import zk.AuCmd0
  260. * @import zk.AuCmd1
  261. * The AU Engine used to send the AU requests to the server and to process
  262. * the AU responses.
  263. */
  264. zAu = {
  265. _resetTimeout: function () { //called by mount.js
  266. if (idTimeout) {
  267. clearTimeout(idTimeout);
  268. idTimeout = null;
  269. }
  270. if (zk.timeout > 0)
  271. idTimeout = setTimeout(sendTimeout, zk.timeout * 1000);
  272. },
  273. _onClientInfo: function () { //Called by mount.js when onReSize
  274. if (zAu._cInfoReg) setTimeout(fireClientInfo, 20);
  275. //we cannot pass zAu.cmd0.clientInfo directly
  276. //otherwise, FF will pass 1 as the firt argument,
  277. //i.e., it is equivalent to zAu.cmd0.clientInfo(1)
  278. },
  279. //Used by mount.js to search widget being detached in this AU
  280. _wgt$: function (uuid) {
  281. var map = _detached.wgts = _detached.wgts || {}, wgt;
  282. while (wgt = _detached.shift())
  283. _wgt2map(wgt, map);
  284. return map[uuid];
  285. },
  286. _onVisibilityChange: function () { //Called by mount.js when page visibility changed
  287. if (zk.visibilitychange) zAu.cmd0.visibilityChange();
  288. },
  289. //Bug ZK-1596: native will be transfer to stub in EE, store the widget for used in mount.js
  290. _storeStub: function (wgt) {
  291. if (wgt)
  292. _detached.push(wgt);
  293. },
  294. //Error Handling//
  295. /** Register a listener that will be called when the Ajax request failed.
  296. * The listener shall be
  297. * <pre><code>function (req, errCode)</code></pre>
  298. *
  299. * where req is an instance of {@link _global_.XMLHttpRequest},
  300. * and errCode is the error code.
  301. * Furthermore, the listener could return true to ignore the error.
  302. * In other words, if true is returned, the error is ignored (the
  303. * listeners registered after won't be called either).
  304. * <p>Notice that req.status might be 200, since ZK might send the error
  305. * back with the ZK-Error header.
  306. *
  307. * <p>To remove the listener, use {@link #unError}.
  308. * @since 5.0.4
  309. * @see #unError
  310. * @see #confirmRetry
  311. */
  312. onError: function (fn) {
  313. _onErrs.push(fn);
  314. },
  315. /** Unregister a listener for handling errors.
  316. * @since 5.0.4
  317. * @see #onError
  318. */
  319. unError: function (fn) {
  320. _onErrs.$remove(fn);
  321. },
  322. /** Called to confirm the user whether to retry, when an error occurs.
  323. * @param String msgCode the message code
  324. * @param String msg2 the additional message. Ignored if not specified or null.
  325. * @return boolean whether to retry
  326. */
  327. confirmRetry: function (msgCode, msg2) {
  328. var msg = msgzk[msgCode];
  329. return jq.confirm((msg ? msg : msgCode) + '\n' + msgzk.TRY_AGAIN + (msg2 ? '\n\n(' + msg2 + ')' : ''));
  330. },
  331. /** Called to shown an error if a severe error occurs.
  332. * By default, it is an orange box.
  333. * @param String msgCode the message code
  334. * @param String msg2 the additional message. Ignored if not specified or null.
  335. * @param String cmd the command causing the problem. Ignored if not specified or null.
  336. * @param Throwable ex the exception
  337. */
  338. showError: function (msgCode, msg2, cmd, ex) {
  339. var msg = msgzk[msgCode];
  340. zk.error((msg ? msg : msgCode) + '\n' + (msg2 ? msg2 + ': ' : '') + (cmd || '')
  341. + (ex ? '\n' + _exmsg(ex) : ''));
  342. },
  343. /** Returns the URI for the specified error.
  344. * @param int code the error code
  345. * @return String the URI.
  346. */
  347. getErrorURI: function (code) {
  348. return zAu._errURIs['' + code];
  349. },
  350. /** Sets the URI for the specified error.
  351. * @param int code the error code
  352. * @param String uri the URI
  353. */
  354. /** Sets the URI for the errors specified in a map.
  355. * @param Map errors A map of errors where the key is the error code (int),
  356. * while the value is the URI (String).
  357. */
  358. setErrorURI: function (code, uri) {
  359. if (arguments.length == 1) {
  360. for (var c in code)
  361. zAu.setErrorURI(c, code[c]);
  362. } else
  363. zAu._errURIs['' + code] = uri;
  364. },
  365. /** Sets the URI for the server-push related error.
  366. * @param int code the error code
  367. * @return String the URI.
  368. */
  369. getPushErrorURI: function (code) {
  370. return _perrURIs['' + code];
  371. },
  372. /** Sets the URI for the server-push related error.
  373. * @param int code the error code
  374. * @param String uri the URI
  375. */
  376. /** Sets the URI for the server-push related errors specified in a map.
  377. * @param Map errors A map of errors where the key is the error code (int),
  378. * while the value is the URI (String).
  379. */
  380. setPushErrorURI: function (code, uri) {
  381. if (arguments.length == 1) {
  382. for (var c in code)
  383. zAu.setPushErrorURI(c, code[c]);
  384. return;
  385. }
  386. _perrURIs['' + code] = uri;
  387. },
  388. ////Ajax Send////
  389. /** Returns whether ZK Client Engine is busy for processing something,
  390. * such as mounting the widgets, processing the AU responses and on.
  391. * @return boolean whether ZK Client Engine is busy
  392. */
  393. processing: function () {
  394. return zk.mounting || cmdsQue.length || zAu.ajaxReq || zAu.pendingReqInf;
  395. },
  396. /** Sends an AU request and appends it to the end if there is other pending
  397. * AU requests.
  398. *
  399. * @param Event aureq the request. If {@link Event#target} is null,
  400. * the request will be sent to each desktop at the client.
  401. * @param int timeout the time (milliseconds) to wait before sending the request.
  402. * 0 is assumed if not specified or negative.
  403. * If negative, the request is assumed to be implicit, i.e., no message will
  404. * be shown if an error occurs.
  405. */
  406. send: function (aureq, timeout) {
  407. //ZK-2790: when unload event is triggered, the desktop is destroyed
  408. //we shouldn't send request back to server
  409. if (zk.unloading && zk.rmDesktoping) //it's safer to check if both zk.unloading and zk.rmDesktoping are true
  410. return;
  411. if (timeout < 0)
  412. aureq.opts = zk.copy(aureq.opts, {defer: true});
  413. var t = aureq.target;
  414. if (t) {
  415. ajaxSend(t.className == 'zk.Desktop' ? t : t.desktop, aureq, timeout);
  416. } else {
  417. var dts = zk.Desktop.all;
  418. for (var dtid in dts)
  419. ajaxSend(dts[dtid], aureq, timeout);
  420. }
  421. },
  422. /** Sends an AU request by placing in front of any other pending request.
  423. * @param Event aureq the request. If {@link Event#target} is null,
  424. * the request will be sent to each desktop at the client.
  425. * @param int timeout the time (milliseconds) to wait before sending the request.
  426. * 0 is assumed if not specified or negative.
  427. * If negative, the request is assumed to be implicit, i.e., no message will
  428. * be shown if an error occurs.
  429. */
  430. sendAhead: function (aureq, timeout) {
  431. var t = aureq.target;
  432. if (t) {
  433. var dt = t.className == 'zk.Desktop' ? t : t.desktop;
  434. zAu.getAuRequests(dt).unshift(aureq);
  435. ajaxSend2(dt, timeout);
  436. } else {
  437. var dts = zk.Desktop.all;
  438. for (var dtid in dts) {
  439. zAu.getAuRequests(dt).unshift(aureq);
  440. ajaxSend2(dts[dtid], timeout);
  441. }
  442. return;
  443. }
  444. },
  445. //remove desktop (used in mount.js and wiget.js)
  446. _rmDesktop: function (dt, dummy) {
  447. var url = zk.ajaxURI(null, {desktop: dt, au: true}),
  448. data = jq.param({dtid: dt.id, cmd_0: dummy ? 'dummy' : 'rmDesktop', opt_0: 'i'}),
  449. headers = {};
  450. if (zk.pfmeter) {
  451. var fakeReq = {
  452. setRequestHeader: function (name, value) {
  453. headers[name] = value;
  454. }
  455. };
  456. zAu._pfsend(dt, fakeReq, true, false);
  457. }
  458. // ZK-4204
  459. if (navigator.sendBeacon && window.URLSearchParams) {
  460. var params = new URLSearchParams(data);
  461. for (var key in headers) {
  462. if (headers.hasOwnProperty(key))
  463. params.append(key, headers[key]);
  464. }
  465. navigator.sendBeacon(url, zk.chrome // https://crbug.com/747787
  466. ? new Blob([params.toString()], {type: 'application/x-www-form-urlencoded'})
  467. : params
  468. );
  469. } else {
  470. this._rmDesktopAjax(url, data, headers);
  471. }
  472. // B65-ZK-2210: clean up portlet2 data when desktop removed.
  473. if (!dummy && zk.portlet2Data && zk.portlet2Data[dt.id]) {
  474. delete zk.portlet2Data[dt.id];
  475. }
  476. },
  477. _rmDesktopAjax: function (url, data, headers) {
  478. jq.ajax(zk.$default({
  479. url: url,
  480. data: data,
  481. beforeSend: function (xhr) {
  482. for (var key in headers) {
  483. if (headers.hasOwnProperty(key))
  484. xhr.setRequestHeader(key, headers[key]);
  485. }
  486. },
  487. //2011/04/22 feature 3291332
  488. //Use sync request for IE, chrome, safari and firefox (4 and later).
  489. //Note: when pressing F5, the request's URL still arrives before this even async:false
  490. async: false
  491. }, zAu.ajaxSettings), null, true/*fixed IE memory issue for jQuery 1.6.x*/);
  492. },
  493. ////Ajax////
  494. /** Processes the AU response sent from the server.
  495. * <p>Don't call it directly at the client.
  496. * @param String cmd the command, such as echo
  497. * @param String data the data in a JSON string.
  498. */
  499. process: function (cmd, data) {
  500. doProcess(cmd, data ? jq.evalJSON(data) : []);
  501. },
  502. /** Returns whether to ignore the ESC keystroke.
  503. * It returns true if ZK Client Engine is sending an AU request
  504. * @return Object
  505. */
  506. shallIgnoreESC: function () {
  507. return zAu.ajaxReq;
  508. },
  509. /** Process the specified commands.
  510. * @param String dtid the desktop's ID
  511. * @param Array rs a list of responses
  512. * @since 5.0.5
  513. */
  514. doCmds: function (dtid, rs) {
  515. var cmds = [];
  516. cmds.dt = zk.Desktop.$(dtid);
  517. pushCmds(cmds, rs);
  518. zAu._doCmds();
  519. },
  520. _doCmds: function (sid) { //called by mount.js, too
  521. for (var fn; fn = doCmdFns.shift();)
  522. fn();
  523. var ex, j = 0, rid = responseId;
  524. for (; j < cmdsQue.length; ++j) {
  525. if (zk.mounting) return; //wait mount.js mtAU to call
  526. var cmds = cmdsQue[j];
  527. if (rid == cmds.rid || !rid || !cmds.rid //match
  528. || zk.Desktop._ndt > 1) { //ignore multi-desktops (risky but...)
  529. cmdsQue.splice(j, 1);
  530. var oldrid = rid;
  531. if (cmds.rid) {
  532. if ((rid = cmds.rid + 1) >= 1000)
  533. rid = 1; //1~999
  534. responseId = rid;
  535. }
  536. try {
  537. if (doCmdsNow(cmds)) { //done
  538. j = -1; //start over
  539. if (zk.pfmeter) {
  540. var fn = function () {zAu._pfdone(cmds.dt, cmds.pfIds);};
  541. if (zk.mounting) doCmdFns.push(fn);
  542. else fn();
  543. }
  544. } else { //not done yet (=zk.mounting)
  545. responseId = oldrid; //restore
  546. cmdsQue.splice(j, 0, cmds); //put it back
  547. return; //wait mount.js mtAU to call
  548. }
  549. } catch (e) {
  550. if (!ex) ex = e;
  551. j = -1; //start over
  552. }
  553. }
  554. }
  555. if (cmdsQue.length) { //sequence is wrong => enforce to run if timeout
  556. setTimeout(function () {
  557. if (cmdsQue.length && rid == responseId) {
  558. var r = cmdsQue[0].rid;
  559. for (j = 1; j < cmdsQue.length; ++j) { //find min
  560. var r2 = cmdsQue[j].rid,
  561. v = r2 - r;
  562. if (v > 500 || (v < 0 && v > -500)) r = r2;
  563. }
  564. responseId = r;
  565. zAu._doCmds(sid);
  566. }
  567. }, 3600);
  568. } else
  569. checkProgressing(sid);
  570. if (ex) throw ex;
  571. },
  572. /** Called before sending an AU request.
  573. * <p>Default: append {@link zk.Widget#autag} to <code>uri</code>.
  574. * <p>It is designed to be overriden by an application to record
  575. * what AU requests have been sent. For example, to work with Google Analytics,
  576. * you can add the following code:
  577. * <pre><code>
  578. &lt;script defer="true">&lt;![CDATA[
  579. var pageTracker = _gat._getTracker("UA-123456");
  580. pageTracker._setDomainName("zkoss.org");
  581. pageTracker._initData();
  582. pageTracker._trackPageview();
  583. var auBfSend = zAu.beforeSend;
  584. zAu.beforeSend = function (uri, req, dt) {
  585. try {
  586. var target = req.target;
  587. if (target.id) {
  588. var data = req.data||{},
  589. value = data.items &amp;&amp; data.items[0]?data.items[0].id:data.value;
  590. pageTracker._trackPageview((target.desktop?target.desktop.requestPath:"") + "/" + target.id + "/" + req.name + (value?"/"+value:""));
  591. }
  592. } catch (e) {
  593. }
  594. return auBfSend(uri, req, dt);
  595. };
  596. ]]>&lt;/script>
  597. *</code></pre>
  598. *
  599. * @param String uri the AU's request URI (such as /zkau)
  600. * @param Event aureq the AU request
  601. * @param Desktop dt the desktop
  602. * @return String the AU's request URI.
  603. * @since 5.0.2
  604. */
  605. beforeSend: function (uri, aureq/*, dt*/) {
  606. var target, tag;
  607. if ((target = aureq.target) && (tag = target.autag)) {
  608. tag = '/' + encodeURIComponent(tag);
  609. if (uri.indexOf('/_/') < 0) {
  610. var v = target.desktop;
  611. if ((v = v ? v.requestPath : '') && v.charAt(0) != '/')
  612. v = '/' + v; //just in case
  613. tag = '/_' + v + tag;
  614. }
  615. var j = uri.lastIndexOf(';');
  616. if (j >= 0) uri = uri.substring(0, j) + tag + uri.substring(j);
  617. else uri += tag;
  618. }
  619. return uri;
  620. },
  621. /** Returns the content to send to the server.
  622. * By default, it is encoded into several parameters and the data
  623. * parameters (data_*) is encoded in JSON.
  624. * <p>If you prefer to encode it into another format, you could override
  625. * this method, and also implement a Java interface called
  626. * <a href="http://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/au/AuDecoder.html">org.zkoss.zk.au.AuDecoder</a>\
  627. * to decode the format at the server.
  628. * <p>If you prefer to encode it into URI, you could override
  629. * {@link #beforeSend}.
  630. * @param int j the order of the AU request. ZK sends a batch of AU
  631. * request at once and this argument indicates the order an AU request is
  632. * (starting from 0).
  633. * @param Event aureq the AU request
  634. * @param Desktop dt the desktop
  635. * @return String the content of the AU request.
  636. * @since 5.0.4
  637. */
  638. encode: function (j, aureq, dt) {
  639. var target = aureq.target,
  640. opts = aureq.opts || {},
  641. portlet2Namespace = '';
  642. // B65-ZK-2210: add porlet namespace
  643. if (zk.portlet2Data && zk.portlet2Data[dt.id]) {
  644. portlet2Namespace = zk.portlet2Data[dt.id].namespace || '';
  645. }
  646. var content = j ? '' : portlet2Namespace + 'dtid=' + dt.id;
  647. content += '&' + portlet2Namespace + 'cmd_' + j + '=' + aureq.name;
  648. if ((opts.implicit || opts.ignorable) && !(opts.serverAlive))
  649. content += '&' + portlet2Namespace + 'opt_' + j + '=i';
  650. //thus, the server will ignore it if session timeout
  651. if (target && target.className != 'zk.Desktop')
  652. content += '&' + portlet2Namespace + 'uuid_' + j + '=' + target.uuid;
  653. var data = aureq.data, dtype = typeof data;
  654. if (dtype == 'string' || dtype == 'number' || dtype == 'boolean' || jq.isArray(data))
  655. data = {'': data};
  656. if (data)
  657. content += '&' + portlet2Namespace + 'data_' + j + '=' + encodeURIComponent(zAu.toJSON(target, data));
  658. return content;
  659. },
  660. /** Enforces all pending AU requests of the specified desktop to send immediately
  661. * @param Desktop dt
  662. * @return boolean whether it is sent successfully. If it has to wait
  663. * for other condition, this method returns false.
  664. */
  665. sendNow: function (dt) {
  666. if (zAu.disabledRequest) {
  667. return false;
  668. }
  669. var es = zAu.getAuRequests(dt);
  670. if (es.length == 0)
  671. return false;
  672. if (zk.mounting) {
  673. zk.afterMount(function () {zAu.sendNow(dt);});
  674. return true; //wait
  675. }
  676. if (zAu.ajaxReq || zAu.pendingReqInf) { //send ajax request one by one
  677. sendPending = true;
  678. return true;
  679. }
  680. //decide implicit (and uri)
  681. var implicit, uri;
  682. for (var j = 0, el = es.length; j < el; ++j) {
  683. var aureq = es[j],
  684. opts = aureq.opts || {};
  685. if (opts.uri != uri) {
  686. if (j) break;
  687. uri = opts.uri;
  688. }
  689. //ignorable and defer implies implicit
  690. if (!(implicit = opts.ignorable || opts.implicit || opts.defer))
  691. break;
  692. }
  693. //notify watches (fckez uses it to ensure its value is sent back correctly
  694. try {
  695. zWatch.fire('onSend', null, implicit);
  696. } catch (e) {
  697. zAu.showError('FAILED_TO_SEND', null, null, e);
  698. }
  699. //decide ignorable
  700. var ignorable = true;
  701. for (var j = 0, el = es.length; j < el; ++j) {
  702. var aureq = es[j],
  703. opts = aureq.opts || {};
  704. if ((opts.uri != uri)
  705. || !(ignorable = ignorable && opts.ignorable)) //all ignorable
  706. break;
  707. }
  708. var forceAjax = false;
  709. for (var j = 0, el = es.length; j < el; ++j) {
  710. var aureq = es[j],
  711. opts = aureq.opts || {};
  712. if (opts.forceAjax) {
  713. forceAjax = true;
  714. break;
  715. }
  716. }
  717. //Consider XML (Pros: ?, Cons: larger packet)
  718. var content, rtags = {},
  719. requri = uri || zk.ajaxURI(null, {desktop: dt,au: true}),
  720. ws = typeof zWs != 'undefined' && zWs.ready;
  721. if (!forceAjax && ws) {
  722. content = {};
  723. } else {
  724. content = '';
  725. }
  726. for (var j = 0, el = es.length; el; ++j, --el) {
  727. var aureq = es.shift();
  728. if ((aureq.opts || {}).uri != uri) {
  729. es.unshift(aureq);
  730. break;
  731. }
  732. requri = zAu.beforeSend(requri, aureq, dt);
  733. if (!forceAjax && ws) {
  734. zk.copy(content, zWs.encode(j, aureq, dt));
  735. } else {
  736. content += zAu.encode(j, aureq, dt);
  737. }
  738. zk.copy(rtags, (aureq.opts || {}).rtags);
  739. }
  740. // B65-ZK-2210: get resourceURL by desktop id
  741. if (zk.portlet2Data && zk.portlet2Data[dt.id]) {
  742. requri = zk.portlet2Data[dt.id].resourceURL;
  743. }
  744. //if (zk.portlet2AjaxURI)
  745. //requri = zk.portlet2AjaxURI;
  746. if (content)
  747. ajaxSendNow({
  748. sid: zAu.seqId, uri: requri, dt: dt, content: content,
  749. implicit: implicit,
  750. ignorable: ignorable, tmout: 0, rtags: rtags, forceAjax: forceAjax
  751. });
  752. return true;
  753. },
  754. /** Add the AU request to the ajax queue.
  755. * @param Desktop dt
  756. * @param Event aureq the request.
  757. * @since 7.0.3
  758. */
  759. addAuRequest: function (dt, aureq) {
  760. if (!dt.obsolete)
  761. dt._aureqs.push(aureq);
  762. },
  763. /** Returns all pending AU requests.
  764. * @param Desktop dt
  765. * @return Array an array of {@link Event}
  766. * @since 7.0.3
  767. */
  768. getAuRequests: function (dt) {
  769. return dt._aureqs;
  770. },
  771. /** A map of Ajax default setting used to send the AU requests.
  772. * @type Map
  773. */
  774. ajaxSettings: zk.$default({
  775. global: false,
  776. //cache: false, //no need to turn off cache since server sends NO-CACHE
  777. contentType: 'application/x-www-form-urlencoded;charset=UTF-8'
  778. }, jq.ajaxSettings),
  779. // Adds performance request IDs that have been processed completely.
  780. // Called by moun.js, too
  781. _pfrecv: function (dt, pfIds) {
  782. zAu.pfAddIds(dt, '_pfRecvIds', pfIds);
  783. },
  784. // Adds performance request IDs that have been processed completely.
  785. // Called by moun.js, too
  786. _pfdone: function (dt, pfIds) {
  787. zAu.pfAddIds(dt, '_pfDoneIds', pfIds);
  788. },
  789. // Sets performance rquest IDs to the request's header
  790. // Called by moun.js, too
  791. _pfsend: function (dt, req, completeOnly, forceAjax) {
  792. var ws = !forceAjax && typeof zWs != 'undefined' && zWs.ready;
  793. if (!completeOnly) {
  794. var dtt = dt.id + '-' + pfIndex++ + '=' + Math.round(jq.now());
  795. req.setRequestHeader('ZK-Client-Start', dtt);
  796. if (ws) {
  797. zWs.setRequestHeaders('ZK-Client-Start', dtt);
  798. }
  799. }
  800. var ids;
  801. if (ids = dt._pfRecvIds) {
  802. req.setRequestHeader('ZK-Client-Receive', ids);
  803. if (ws) {
  804. zWs.setRequestHeaders('ZK-Client-Receive', ids);
  805. }
  806. dt._pfRecvIds = null;
  807. }
  808. if (ids = dt._pfDoneIds) {
  809. req.setRequestHeader('ZK-Client-Complete', ids);
  810. if (ws) {
  811. zWs.setRequestHeaders('ZK-Client-Complete', ids);
  812. }
  813. dt._pfDoneIds = null;
  814. }
  815. },
  816. /** Creates widgets based on an array of JavaScritp codes generated by
  817. * Component.redraw() at the server.
  818. * <p>This method is usually used with Java's ComponentsCtrl.redraw, and
  819. * {@link Widget#replaceCavedChildren_}.
  820. * <p>Notice that, since the creation of widgets might cause some packages
  821. * to be loaded, the callback function, fn, might be called after this
  822. * method is returned
  823. * @param Array codes an array of JavaScript objects generated at the server.
  824. * For example, <code>smartUpdate("foo", ComponentsCtrl.redraw(getChildren());</code>
  825. * @param Function fn the callback function. When the widgets are created.
  826. * <code>fn</code> is called with an array of {@link Widget}. In other words,
  827. * the callback's signature is as follows:<br/>
  828. * <code>void callback(zk.Widget[] wgts);</code>
  829. * @param Function filter the filter to avoid the use of widgets being replaced.
  830. * Ignored if null
  831. * @since 5.0.2
  832. */
  833. createWidgets: function (codes, fn, filter) {
  834. //bug #3005632: Listbox fails to replace with empty model if in ROD mode
  835. var wgts = [], len = codes.length;
  836. if (len > 0) {
  837. for (var j = 0; j < len; ++j)
  838. zkx_(codes[j], function (newwgt) {
  839. wgts.push(newwgt);
  840. if (wgts.length == len)
  841. fn(wgts);
  842. }, filter);
  843. } else
  844. fn(wgts);
  845. },
  846. /* (not jsdoc)
  847. * Shows or clear an error message. It is overriden by zul.wpd.
  848. * <p>wrongValue_(wgt, msg): show an error message
  849. * <p>wrongValue_(wgt, false): clear an error message
  850. */
  851. wrongValue_: function (wgt, msg) {
  852. if (msg !== false)
  853. jq.alert(msg);
  854. },
  855. // Called when the response is received from zAu.ajaxReq.
  856. _onResponseReady: function () {
  857. var req = zAu.ajaxReq, reqInf = zAu.ajaxReqInf, sid;
  858. try {
  859. if (req && req.readyState == 4) {
  860. zAu.ajaxReq = zAu.ajaxReqInf = null;
  861. if (zk.pfmeter) zAu._pfrecv(reqInf.dt, zAu.pfGetIds(req));
  862. sid = req.getResponseHeader('ZK-SID');
  863. var rstatus;
  864. if ((rstatus = req.status) == 200) { //correct
  865. if (zAu._respSuccess(req, reqInf, sid)) return;
  866. } else if ((!sid || sid == zAu.seqId) //ignore only if out-of-seq (note: 467 w/o sid)
  867. && !zAu.onResponseError(req, zAu._errCode = rstatus)) {
  868. if (zAu._respFailure(req, reqInf, rstatus)) return;
  869. }
  870. }
  871. } catch (e) {
  872. if (zAu._respException(req, reqInf, e)) return;
  873. }
  874. zAu.afterResponse(sid);
  875. },
  876. _respSuccess: function (req, reqInf, sid) {
  877. if (sid && sid != zAu.seqId) {
  878. zAu._errCode = 'ZK-SID ' + (sid ? 'mismatch' : 'required');
  879. zAu.afterResponse(); //continue the pending request if any
  880. return true;
  881. } //if sid null, always process (usually for error msg)
  882. var v;
  883. if ((v = req.getResponseHeader('ZK-Error'))
  884. && !zAu.onResponseError(req, v = zk.parseInt(v) || v)
  885. && (v == 5501 || v == 5502) //Handle only ZK's SC_OUT_OF_SEQUENCE or SC_ACTIVATION_TIMEOUT
  886. && zAu.confirmRetry('FAILED_TO_RESPONSE',
  887. v == 5501 ? 'Request out of sequence' : 'Activation timeout')) {
  888. zAu.ajaxReqResend(reqInf);
  889. return true;
  890. }
  891. if (v != 410 //not timeout (SC_GONE)
  892. && !(reqInf.rtags && reqInf.rtags.isDummy) //ZK-3304: dummy request shouldn't reset timeout
  893. && (!reqInf.rtags || !reqInf.rtags.onTimer || zk.timerAlive)) // Bug ZK-2720 only timer-keep-alive should reset the timeout
  894. zAu._resetTimeout();
  895. if (zAu.pushReqCmds(reqInf, req)) { //valid response
  896. //advance SID to avoid receive the same response twice
  897. if (sid && ++zAu.seqId > 9999) zAu.seqId = 1;
  898. zAu.ajaxReqTries = 0;
  899. zAu.pendingReqInf = null;
  900. }
  901. },
  902. _respFailure: function (req, reqInf, rstatus) {
  903. var eru = zAu._errURIs['' + rstatus];
  904. if (typeof eru == 'string') {
  905. zUtl.go(eru);
  906. return true;
  907. }
  908. if (typeof zAu.ajaxErrorHandler == 'function') {
  909. zAu.ajaxReqTries = zAu.ajaxErrorHandler(req, rstatus, req.statusText, zAu.ajaxReqTries);
  910. if (zAu.ajaxReqTries > 0) {
  911. zAu.ajaxReqTries--;
  912. zAu.ajaxReqResend(reqInf, zk.resendTimeout);
  913. return true;
  914. }
  915. } else {
  916. //handle MSIE's buggy HTTP status codes
  917. //http://msdn2.microsoft.com/en-us/library/aa385465(VS.85).aspx
  918. switch (rstatus) { //auto-retry for certain case
  919. default:
  920. if (!zAu.ajaxReqTries) break;
  921. //fall thru
  922. case 12002: //server timeout
  923. case 12030: //http://danweber.blogspot.com/2007/04/ie6-and-error-code-12030.html
  924. case 12031:
  925. case 12152: // Connection closed by server.
  926. case 12159:
  927. case 13030:
  928. case 503: //service unavailable
  929. if (!zAu.ajaxReqTries) zAu.ajaxReqTries = 3; //two more try
  930. if (--zAu.ajaxReqTries) {
  931. zAu.ajaxReqResend(reqInf, zk.resendTimeout);
  932. return true;
  933. }
  934. }
  935. if (!reqInf.ignorable && !zk.unloading) {
  936. var msg = req.statusText;
  937. if (zAu.confirmRetry('FAILED_TO_RESPONSE', rstatus + (msg ? ': ' + msg : ''))) {
  938. zAu.ajaxReqTries = 2; //one more try
  939. zAu.ajaxReqResend(reqInf);
  940. return true;
  941. }
  942. }
  943. }
  944. },
  945. _respException: function (req, reqInf, e) {
  946. if (!window.zAu)
  947. return true; //the doc has been unloaded
  948. zAu.ajaxReq = zAu.ajaxReqInf = null;
  949. try {
  950. if (req && typeof req.abort == 'function') req.abort();
  951. } catch (e2) {
  952. zk.debugLog(e2.message || e2);
  953. }
  954. //NOTE: if connection is off and req.status is accessed,
  955. //Mozilla throws exception while IE returns a value
  956. if (reqInf && !reqInf.ignorable && !zk.unloading) {
  957. var msg = _exmsg(e);
  958. zAu._errCode = '[Receive] ' + msg;
  959. //if (e.fileName) _errCode += ", "+e.fileName;
  960. //if (e.lineNumber) _errCode += ", "+e.lineNumber;
  961. if (zAu.confirmRetry('FAILED_TO_RESPONSE', (msg && msg.indexOf('NOT_AVAILABLE') < 0 ? msg : ''))) {
  962. zAu.ajaxReqResend(reqInf);
  963. return true;
  964. }
  965. }
  966. },
  967. /** The AU command handler that handles commands not related to widgets.
  968. * @type AuCmd0
  969. */
  970. //cmd0: null, //jsdoc
  971. /** The AU command handler that handles commands releated to widgets.
  972. * @type AuCmd1
  973. */
  974. //cmd1: null, //jsdoc
  975. pushReqCmds: function (reqInf, req) {
  976. var dt = reqInf.dt,
  977. rt = req.responseText;
  978. if (!rt) {
  979. if (zk.pfmeter) zAu._pfdone(dt, zAu.pfGetIds(req));
  980. return false; //invalid
  981. }
  982. var cmds = [];
  983. cmds.rtags = reqInf.rtags;
  984. if (zk.pfmeter) {
  985. cmds.dt = dt;
  986. cmds.pfIds = zAu.pfGetIds(req);
  987. }
  988. try {
  989. rt = jq.evalJSON(rt);
  990. } catch (e) {
  991. if (e.name == 'SyntaxError') { //ZK-4199: handle json parse error
  992. zAu.showError('FAILED_TO_PARSE_RESPONSE', e.message);
  993. zk.debugLog(e.message + ', response text:\n' + req.responseText);
  994. return false;
  995. }
  996. throw e;
  997. }
  998. var rid = rt.rid;
  999. if (rid) {
  1000. rid = parseInt(rid); //response ID
  1001. if (!isNaN(rid)) cmds.rid = rid;
  1002. }
  1003. pushCmds(cmds, rt.rs);
  1004. return true;
  1005. },
  1006. /* internal use only */
  1007. afterResponse: function (sid) {
  1008. zAu._doCmds(sid); //invokes checkProgressing
  1009. //handle pending ajax send
  1010. if (sendPending && !zAu.ajaxReq && !zAu.pendingReqInf) {
  1011. sendPending = false;
  1012. var dts = zk.Desktop.all;
  1013. for (var dtid in dts)
  1014. ajaxSend2(dts[dtid], 0);
  1015. }
  1016. },
  1017. /* @param zk.Widget target
  1018. */
  1019. toJSON: function (target, data) {
  1020. if (!jq.isArray(data)) {
  1021. if (data.pageX != null && data.x == null) {
  1022. var ofs = target && target.desktop ? // B50-3336745: target may have been detached
  1023. target.fromPageCoord(data.pageX, data.pageY) :
  1024. [data.pageX, data.pageY];
  1025. data.x = ofs[0];
  1026. data.y = ofs[1];
  1027. }
  1028. var v;
  1029. for (var n in data)
  1030. if ((v = data[n]) instanceof DateImpl)
  1031. data[n] = '$z!t#d:' + jq.d2j(v);
  1032. }
  1033. return jq.toJSON(data);
  1034. },
  1035. /* internal use only */
  1036. ajaxReq: null,
  1037. /* internal use only */
  1038. ajaxReqInf: null,
  1039. /* internal use only */
  1040. ajaxReqTries: null,
  1041. /* internal use only */
  1042. seqId: (jq.now() % 9999) + 1,
  1043. /* internal use only */
  1044. pendingReqInf: null,
  1045. _errCode: null,
  1046. _errURIs: {},
  1047. //Perfomance Meter//
  1048. // Returns request IDs sent from the server separated by space.
  1049. pfGetIds: function (req) {
  1050. return req.getResponseHeader('ZK-Client-Complete');
  1051. },
  1052. pfAddIds: function (dt, prop, pfIds) {
  1053. if (pfIds && (pfIds = pfIds.trim())) {
  1054. var s = pfIds + '=' + Math.round(jq.now());
  1055. if (dt[prop]) dt[prop] += ',' + s;
  1056. else dt[prop] = s;
  1057. }
  1058. },
  1059. ajaxReqResend: function (reqInf, timeout) {
  1060. if (zAu.seqId == reqInf.sid) {//skip if the response was recived
  1061. zAu.pendingReqInf = reqInf; //store as a pending request info
  1062. setTimeout(ajaxReqResend2, timeout ? timeout : 0);
  1063. }
  1064. },
  1065. onResponseError: function (req, errCode) {
  1066. //$clone first since it might add or remove onError
  1067. for (var errs = _onErrs.$clone(), fn; fn = errs.shift();)
  1068. if (fn(req, errCode))
  1069. return true; //ignored
  1070. }
  1071. };
  1072. /** @partial zAu
  1073. */
  1074. //@{
  1075. /** Implements this function to be called if the request fails.
  1076. * The function receives four arguments: The XHR (XMLHttpRequest) object,
  1077. * a number describing the status of the request, a string describing the text
  1078. * of the status, and a number describing the retry value to re-send.
  1079. *
  1080. * <p>For example,
  1081. <pre><code>
  1082. zAu.ajaxErrorHandler = function (req, status, statusText, ajaxReqTries) {
  1083. if (ajaxReqTries == null)
  1084. ajaxReqTries = 3; // retry 3 times
  1085. // reset the resendTimeout, for more detail, please refer to
  1086. // http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_client-config_Element/The_auto-resend-timeout_Element
  1087. zk.resendTimeout = 2000;//wait 2 seconds to resend.
  1088. if (!zAu.confirmRetry("FAILED_TO_RESPONSE", status+(statusText?": "+statusText:"")))
  1089. return 0; // no retry;
  1090. return ajaxReqTries;
  1091. }
  1092. </code></pre>
  1093. * @param Object req the object of XMLHttpRequest
  1094. * @param int status the status of the request
  1095. * @param String statusText the text of the status from the request
  1096. * @param int ajaxReqTries the retry value for re-sending the request, if undefined
  1097. * means the function is invoked first time.
  1098. * @since 6.5.2
  1099. */
  1100. //ajaxErrorHandler: function () {}
  1101. //@};
  1102. //Commands//
  1103. /** @class zk.AuCmd0
  1104. * The AU command handler for processes commands not related to widgets,
  1105. * sent from the server.
  1106. * @see zAu#cmd0
  1107. */
  1108. zAu.cmd0 = /*prototype*/ { //no uuid at all
  1109. /** Sets a bookmark
  1110. * @param String bk the bookmark
  1111. * @param boolean replace if true, it will replace the bookmark without creating
  1112. * a new one history.
  1113. */
  1114. bookmark: function (bk, replace) {
  1115. zk.bmk.bookmark(bk, replace);
  1116. },
  1117. /** Shows an error to indicate the desktop is timeout.
  1118. * @param String dtid the desktop UUID
  1119. * @param String msg the error message
  1120. */
  1121. obsolete: function (dtid, msg) {
  1122. var v = zk.Desktop.$(dtid);
  1123. if (v) v.obsolete = true;
  1124. if (msg.startsWith('script:'))
  1125. return $eval(msg.substring(7));
  1126. // ZK-2397: prevent from showing reload dialog again while browser is reloading
  1127. if (zk._isReloadingInObsolete)
  1128. return;
  1129. if (v && (v = v.requestPath))
  1130. msg = msg.replace(dtid, v + ' (' + dtid + ')');
  1131. zAu.disabledRequest = true;
  1132. jq.alert(msg, {
  1133. icon: 'ERROR',
  1134. button: {
  1135. Reload: function () {
  1136. zk._isReloadingInObsolete = true;
  1137. location.reload();
  1138. },
  1139. Cancel: true
  1140. }
  1141. });
  1142. },
  1143. /** Shows an alert to indicate some error occurs.
  1144. * For widget's error message, use {@link #wrongValue} instead.
  1145. * @param String msg the error message
  1146. */
  1147. alert: function (msg, title, icon, disabledAuRequest) {
  1148. if (disabledAuRequest)
  1149. zAu.disabledRequest = true;
  1150. jq.alert(msg, {icon: icon || 'ERROR', title: title});
  1151. },
  1152. /** Redirects to the specified URL.
  1153. * @param String url the URL to redirect to
  1154. * @param String target [optional] the window name to show the content
  1155. * of the URL. If omitted, it will replace the current content.
  1156. */
  1157. redirect: function (url, target) {
  1158. try {
  1159. zUtl.go(url, {target: target});
  1160. // '#' for bookmark change only, Bug ZK-2874
  1161. var idx;
  1162. if (url && !url.startsWith('/') && (idx = url.indexOf('#')) >= 0) {
  1163. var uri = url.substring(0, idx),
  1164. hash = url.substring(idx + 1),
  1165. locHash = window.location.hash,
  1166. locUrl = window.location.href;
  1167. if (locHash) {
  1168. locUrl = locUrl.substring(0, locUrl.length - locHash.length); // excluding '#'
  1169. }
  1170. if (locUrl.endsWith(uri))
  1171. return; // not to disable request for Bug ZK-2844
  1172. }
  1173. // Bug ZK-2844
  1174. if (!target)
  1175. zAu.disabledRequest = true; // Bug ZK-2616
  1176. } catch (ex) {
  1177. if (!zk.confirmClose) throw ex;
  1178. }
  1179. },
  1180. /** Changes the brower window's titile.
  1181. * @param String title the new title
  1182. */
  1183. title: function (title) {
  1184. document.title = title;
  1185. },
  1186. /** Logs the message.
  1187. * @param String msg the message to log
  1188. * @since 5.0.8
  1189. */
  1190. log: zk.log,
  1191. /** Executes the JavaScript.
  1192. * @param String script the JavaScript code snippet to execute
  1193. */
  1194. script: function (script) {
  1195. jq.globalEval(script);
  1196. },
  1197. /** Asks the client to echo back an AU request, such that
  1198. * the server can return other commands.
  1199. * It is used to give the end user a quick response before doing
  1200. * a long operation.
  1201. * @param String dtid the desktop ID ({@link zk.Desktop}).
  1202. * @see zk.AuCmd1#echo2
  1203. * @see #echoGx
  1204. */
  1205. echo: function (dtid) {
  1206. var dt = zk.Desktop.$(dtid),
  1207. aureqs = zAu.getAuRequests(dt);
  1208. // Bug ZK-2741
  1209. for (var i = 0, j = aureqs.length; i < j; i++) {
  1210. var aureq0 = aureqs[i];
  1211. if ((!aureq0.target || aureq0.target.$instanceof(zk.Desktop)) && aureq0.name == 'dummy') {
  1212. return; //no need to send more
  1213. }
  1214. }
  1215. zAu.send(new zk.Event(dt, 'dummy', null, {ignorable: true, rtags: {isDummy: true}}));
  1216. },
  1217. /** Ask the client to echo back globally.
  1218. * <p>Unlike {@link #echo}, it will search all browser windows for
  1219. * <p>Note: this feature requires ZK EE
  1220. * the given desktop IDs.
  1221. * @param String evtnm the event name
  1222. * @param String data any string-typed data
  1223. * @param String... any number of desktop IDs.
  1224. * @since 5.0.4
  1225. */
  1226. //echoGx: function () {}
  1227. /** Asks the client information.
  1228. * The client will reply the information in the <code>onClientInfo</code> response.
  1229. * @param String dtid the desktop ID ({@link zk.Desktop}).
  1230. */
  1231. clientInfo: function (dtid) {
  1232. zAu._cInfoReg = true;
  1233. var orient = '',
  1234. dpr = 1;
  1235. if (zk.mobile) {
  1236. //change default portrait definition because landscape is the default orientation for this device/browser.
  1237. if ((_initLandscape && _initDefault) || (!_initLandscape && !_initDefault))
  1238. _portrait = {'-90': true, '90': true};
  1239. orient = _portrait[window.orientation] ? 'portrait' : 'landscape';
  1240. } else {
  1241. orient = jq.innerWidth() > jq.innerHeight() ? 'landscape' : 'portrait';
  1242. }
  1243. if (window.devicePixelRatio)
  1244. dpr = window.devicePixelRatio;
  1245. var clientInfo = [new Date().getTimezoneOffset(),
  1246. screen.width, screen.height, screen.colorDepth,
  1247. jq.innerWidth(), jq.innerHeight(), jq.innerX(), jq.innerY(), dpr.toFixed(1), orient,
  1248. zk.mm.tz.guess()
  1249. ];
  1250. // ZK-3181: only send when value changed
  1251. var oldClientInfo = zAu._clientInfo;
  1252. if (oldClientInfo) {
  1253. var same = oldClientInfo.every(function (el, index) {
  1254. return el === clientInfo[index];
  1255. });
  1256. if (same) return;
  1257. }
  1258. zAu._clientInfo = clientInfo;
  1259. zAu.send(new zk.Event(zk.Desktop.$(dtid), 'onClientInfo',
  1260. zAu._clientInfo,
  1261. {implicit: true, rtags: {onClientInfo: 1}}));
  1262. },
  1263. visibilityChange: function (dtid) {
  1264. var hidden = !!(document.hidden || document[zk.vendor_ + 'Hidden']),
  1265. visibilityState = document.visibilityState || document[zk.vendor_ + 'VisibilityState'];
  1266. zAu.send(new zk.Event(zk.Desktop.$(dtid), 'onVisibilityChange',
  1267. {hidden: hidden, visibilityState: visibilityState}, {implicit: true, ignorable: true}));
  1268. },
  1269. /** Asks the client to download the resource at the specified URL.
  1270. * @param String url the URL to download from
  1271. */
  1272. download: function (url) {
  1273. if (url) {
  1274. var ifr = jq('#zk_download')[0],
  1275. ie = zk.ie,
  1276. sbu = zk.skipBfUnload;
  1277. if (ie) zk.skipBfUnload = true;
  1278. if (!ifr) {
  1279. ifr = document.createElement('iframe');
  1280. ifr.id = ifr.name = 'zk_download';
  1281. ifr.style.display = 'none';
  1282. ifr.style.width = ifr.style.height = ifr.style.border = ifr.frameBorder = '0';
  1283. document.body.appendChild(ifr);
  1284. }
  1285. if (ie < 11) { // Since IE11 dropped onreadystatechange support: https://stackoverflow.com/a/26835889
  1286. // Use onreadystatechange to listen if iframe is loaded
  1287. ifr.onreadystatechange = function () {
  1288. var state = ifr.contentWindow.document.readyState;
  1289. if (state === 'interactive')
  1290. setTimeout(function () { zk.skipBfUnload = sbu; }, 0);
  1291. };
  1292. }
  1293. ifr.src = url; //It is OK to reuse the same iframe
  1294. // Workaround for IE11: wait a second (not perfect) for iframe loading
  1295. if (ie === 11)
  1296. setTimeout(function () { zk.skipBfUnload = sbu; }, 1000);
  1297. }
  1298. },
  1299. /** Prints the content of the browser window.
  1300. */
  1301. print: function () {
  1302. window.print();
  1303. },
  1304. /** Scrolls the content of the browser window.
  1305. * @param int x the offset (difference) in the X coordinate (horizontally) (pixels)
  1306. * @param int y the offset in the Y coordinate (vertically) (pixels)
  1307. */
  1308. scrollBy: function (x, y) {
  1309. window.scrollBy(x, y);
  1310. },
  1311. /** Scrolls the contents of the browser window to the specified location.
  1312. * @param int x the X coordinate to scroll to (pixels)
  1313. * @param int y the Y coordinate to scroll to (pixels)
  1314. */
  1315. scrollTo: function (x, y) {
  1316. window.scrollTo(x, y);
  1317. },
  1318. /** Resizes the browser window.
  1319. * @param int x the number of pixels to increase/decrease (pixels)
  1320. * @param int y the number of pixels to increase/decrease (pixels)
  1321. */
  1322. resizeBy: function (x, y) {
  1323. window.resizeBy(x, y);
  1324. },
  1325. /** Resizes the browser window to the specified size.
  1326. * @param int x the required width (pixels)
  1327. * @param int y the required height (pixels)
  1328. */
  1329. resizeTo: function (x, y) {
  1330. window.resizeTo(x, y);
  1331. },
  1332. /** Moves the browser window.
  1333. * @param int x the number of pixels to move in the X coordinate
  1334. * @param int y the number of pixels to move in the Y coordinate
  1335. */
  1336. moveBy: function (x, y) {
  1337. window.moveBy(x, y);
  1338. },
  1339. /** Moves the browser window to the specified location
  1340. * @param int x the left (pixels)
  1341. * @param int y the top (pixels)
  1342. */
  1343. moveTo: function (x, y) {
  1344. window.moveTo(x, y);
  1345. },
  1346. /** Sets the message used to confirm the user when he is closing
  1347. * the browser window.
  1348. * @param String msg the message to show in the confirm dialog
  1349. */
  1350. cfmClose: function (msg) {
  1351. zk.confirmClose = msg;
  1352. },
  1353. /** Shows a notification popup.
  1354. * @param String msg message to show
  1355. * @param String type the notification type (warning, info, error)
  1356. * @param String pid uuid of the page to which it belongs
  1357. * @param String ref uuid of a reference component
  1358. * @param String pos the position of notification
  1359. * @param Offset off the offset of x and y
  1360. * @param int dur the duration of notification
  1361. * @param boolean closable the close button of notification
  1362. */
  1363. showNotification: function (msg, type, pid, ref, pos, off, dur, closable) {
  1364. var notif = (zul && zul.wgt) ? zul.wgt.Notification : null; // in zul
  1365. if (notif) {
  1366. var opts = {ref: ref, pos: pos, off: off, dur: dur, type: type, closable: closable};
  1367. //ZK-2687, show notif after zAu.cmd0.scrollIntoView
  1368. zk.delayFunction(ref ? ref.uuid : 'nouuid', function () {
  1369. notif.show(msg, pid, opts);
  1370. });
  1371. } else {
  1372. // TODO: provide a hook to customize
  1373. jq.alert(msg); // fall back to alert when zul is not available
  1374. }
  1375. },
  1376. /** Shows the busy message covering the specified widget.
  1377. * @param String uuid the component's UUID
  1378. * @param String msg the message.
  1379. */
  1380. /** Shows the busy message covering the whole browser window.
  1381. * @param String msg the message.
  1382. */
  1383. showBusy: function (uuid, msg) {
  1384. if (arguments.length == 1) {
  1385. msg = uuid;
  1386. uuid = null;
  1387. }
  1388. zAu.cmd0.clearBusy(uuid);
  1389. var w = uuid ? Widget.$(uuid) : null;
  1390. if (!uuid) {
  1391. zk._prevFocus = zk.currentFocus;
  1392. zUtl.progressbox('zk_showBusy', msg || msgzk.PLEASE_WAIT, true, null, {busy: true});
  1393. } else if (w) {
  1394. zk.delayFunction(uuid, function () {
  1395. w.effects_.showBusy = new zk.eff.Mask({
  1396. id: w.uuid + '-shby',
  1397. anchor: w.$n(),
  1398. message: msg
  1399. });
  1400. });
  1401. }
  1402. },
  1403. /** Removes the busy message covering the specified widget.
  1404. * @param String uuid the component's UUID
  1405. */
  1406. /** Removes the busy message covering the whole browser.
  1407. */
  1408. clearBusy: function (uuid) {
  1409. if (uuid) {
  1410. zk.delayFunction(uuid, function () {
  1411. var w = Widget.$(uuid),
  1412. efs = w ? w.effects_ : null;
  1413. if (efs && efs.showBusy) {
  1414. efs.showBusy.destroy();
  1415. delete efs.showBusy;
  1416. }
  1417. });
  1418. } else {
  1419. zUtl.destroyProgressbox('zk_showBusy', {busy: true}); //since user might want to show diff msg
  1420. if (zk._prevFocus) {
  1421. zk.currentFocus = zk._prevFocus;
  1422. zk._prevFocus = null;
  1423. var wgt = zk.currentFocus;
  1424. try {
  1425. zk._focusByClearBusy = true;
  1426. wgt.focus();
  1427. } finally {
  1428. zk._focusByClearBusy = false;
  1429. }
  1430. }
  1431. }
  1432. },
  1433. /** Closes the all error messages related to the specified widgets.
  1434. * It assumes {@link zk.Widget} has a method called <code>clearErrorMessage</code>
  1435. * (such as {@link zul.inp.InputWidget#clearErrorMessage}).
  1436. * If no such method, nothing happens.
  1437. * @param String... any number of UUID of widgets.
  1438. * @see #wrongValue
  1439. */
  1440. clearWrongValue: function () {
  1441. for (var i = arguments.length; i--;) {
  1442. var wgt = Widget.$(arguments[i]);
  1443. if (wgt) {
  1444. var toClearErrMsg = function (w) {
  1445. return function () {
  1446. if (w.clearErrorMessage) w.clearErrorMessage();
  1447. else zAu.wrongValue_(w, false);
  1448. };
  1449. };
  1450. zk.delayFunction(wgt.uuid, toClearErrMsg(wgt));
  1451. }
  1452. }
  1453. },
  1454. /** Shows the error messages for the specified widgets.
  1455. * It assumes {@link zk.Widget} has a method called <code>setErrorMessage</code>
  1456. * (such as {@link zul.inp.InputWidget#setErrorMessage}).
  1457. * If no such method, {@link jq#alert} is used instead.
  1458. * @param Object... the widgets and messages. The first argument
  1459. * is the widget's UUID, and the second is the error message.
  1460. * The third is UUID, then the fourth the error message, and so on.
  1461. * @see #clearWrongValue
  1462. */
  1463. wrongValue: function () {
  1464. var args = arguments;
  1465. for (var i = 0, len = args.length - 1; i < len; i += 2) {
  1466. var uuid = args[i], msg = args[i + 1],
  1467. wgt = Widget.$(uuid);
  1468. if (wgt) {
  1469. //ZK-2687: create a closure to record the current wgt
  1470. var toSetErrMsg = function (w, m) {
  1471. return function () {
  1472. zk.afterAnimate(function () {
  1473. if (w.setErrorMessage) w.setErrorMessage(m);
  1474. else zAu.wrongValue_(w, m);
  1475. }, -1);
  1476. };
  1477. };
  1478. zk.delayFunction(uuid, toSetErrMsg(wgt, msg));
  1479. } else if (!uuid) //keep silent if component (of uuid) not exist (being detaced)
  1480. jq.alert(msg);
  1481. }
  1482. // for a bug fixed of B60-ZK-1208, we need to delay the func for this test case, B36-2935398.zul
  1483. // has been removed since 7.0.6
  1484. },
  1485. /** Submit a form.
  1486. * This method looks for the widget first. If found and the widget
  1487. * has a method called <code>submit</code>, then the widget's <code>submit</code>
  1488. * method is called. Otherwise, it looks for the DOM element
  1489. * and invokes the <code>submit</code> method (i.e., assume it is
  1490. * the FROM element).
  1491. * @param String id the UUID of the widget, or the ID of the FORM element.
  1492. */
  1493. submit: function (id) {
  1494. setTimeout(function () {
  1495. var n = Widget.$(id);
  1496. if (n && n.submit)
  1497. n.submit();
  1498. else
  1499. zk(id).submit();
  1500. }, 50);
  1501. },
  1502. /** Scrolls the widget or an DOM element into the view
  1503. * @param String id the UUID of the widget, or the ID of the DOM element.
  1504. */
  1505. scrollIntoView: function (id) {
  1506. if (!id) return;
  1507. var w = Widget.$(id);
  1508. if (w) {
  1509. zk.delayFunction(w.uuid, function () {
  1510. w.scrollIntoView();
  1511. });
  1512. } else {
  1513. var zkjq = zk(id);
  1514. if (zkjq.$()) {
  1515. zk.delayFunction(zkjq.$().uuid, function () {
  1516. zk