winlin.utility.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. // winlin.utility.js
  2. /**
  3. * common utilities
  4. * depends: jquery1.10
  5. * https://gitee.com/winlinvip/codes/rpn0c2ewbomj81augzk4y59
  6. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  7. * v 1.0.23
  8. */
  9. /**
  10. * padding the output.
  11. * padding(3, 5, '0') is 00003
  12. * padding(3, 5, 'x') is xxxx3
  13. * @see http://blog.csdn.net/win_lin/article/details/12065413
  14. */
  15. function padding(number, length, prefix) {
  16. if(String(number).length >= length){
  17. return String(number);
  18. }
  19. return padding(prefix+number, length, prefix);
  20. }
  21. /**
  22. * extends system array, to remove all specified elem.
  23. * @param arr the array to remove elem from.
  24. * @param elem the elem to remove.
  25. * @remark all elem will be removed.
  26. * for example,
  27. * arr = [10, 15, 20, 30, 20, 40]
  28. * system_array_remove(arr, 10) // arr=[15, 20, 30, 20, 40]
  29. * system_array_remove(arr, 20) // arr=[15, 30, 40]
  30. */
  31. function system_array_remove(arr, elem) {
  32. if (!arr) {
  33. return;
  34. }
  35. var removed = true;
  36. var i = 0;
  37. while (removed) {
  38. removed = false;
  39. for (; i < arr.length; i++) {
  40. if (elem == arr[i]) {
  41. arr.splice(i, 1);
  42. removed = true;
  43. break;
  44. }
  45. }
  46. }
  47. }
  48. /**
  49. * whether the array contains specified element.
  50. * @param arr the array to find.
  51. * @param elem_or_function the element value or compare function.
  52. * @returns true contains elem; otherwise false.
  53. * for example,
  54. * arr = [10, 15, 20, 30, 20, 40]
  55. * system_array_contains(arr, 10) // true
  56. * system_array_contains(arr, 11) // false
  57. * system_array_contains(arr, function(elem){return elem == 30;}); // true
  58. * system_array_contains(arr, function(elem){return elem == 60;}); // false
  59. */
  60. function system_array_contains(arr, elem_or_function) {
  61. return system_array_get(arr, elem_or_function) != null;
  62. }
  63. /**
  64. * get the specified element from array
  65. * @param arr the array to find.
  66. * @param elem_or_function the element value or compare function.
  67. * @returns the matched elem; otherwise null.
  68. * for example,
  69. * arr = [10, 15, 20, 30, 20, 40]
  70. * system_array_get(arr, 10) // 10
  71. * system_array_get(arr, 11) // null
  72. * system_array_get(arr, function(elem){return elem == 30;}); // 30
  73. * system_array_get(arr, function(elem){return elem == 60;}); // null
  74. */
  75. function system_array_get(arr, elem_or_function) {
  76. for (var i = 0; i < arr.length; i++) {
  77. if (typeof elem_or_function == "function") {
  78. if (elem_or_function(arr[i])) {
  79. return arr[i];
  80. }
  81. } else {
  82. if (elem_or_function == arr[i]) {
  83. return arr[i];
  84. }
  85. }
  86. }
  87. return null;
  88. }
  89. /**
  90. * to iterate on array.
  91. * @param arr the array to iterate on.
  92. * @param pfn the function to apply on it. return false to break loop.
  93. * for example,
  94. * arr = [10, 15, 20, 30, 20, 40]
  95. * system_array_foreach(arr, function(elem, index){
  96. * console.log('index=' + index + ',elem=' + elem);
  97. * });
  98. * @return true when iterate all elems.
  99. */
  100. function system_array_foreach(arr, pfn) {
  101. if (!pfn) {
  102. return false;
  103. }
  104. for (var i = 0; i < arr.length; i++) {
  105. if (!pfn(arr[i], i)) {
  106. return false;
  107. }
  108. }
  109. return true;
  110. }
  111. /**
  112. * whether the str starts with flag.
  113. */
  114. function system_string_startswith(str, flag) {
  115. if (typeof flag == "object" && flag.constructor == Array) {
  116. for (var i = 0; i < flag.length; i++) {
  117. if (system_string_startswith(str, flag[i])) {
  118. return true;
  119. }
  120. }
  121. }
  122. return str && flag && str.length >= flag.length && str.indexOf(flag) == 0;
  123. }
  124. /**
  125. * whether the str ends with flag.
  126. */
  127. function system_string_endswith(str, flag) {
  128. if (typeof flag == "object" && flag.constructor == Array) {
  129. for (var i = 0; i < flag.length; i++) {
  130. if (system_string_endswith(str, flag[i])) {
  131. return true;
  132. }
  133. }
  134. }
  135. return str && flag && str.length >= flag.length && str.indexOf(flag) == str.length - flag.length;
  136. }
  137. /**
  138. * trim the start and end of flag in str.
  139. * @param flag a string to trim.
  140. */
  141. function system_string_trim(str, flag) {
  142. if (!flag || !flag.length || typeof flag != "string") {
  143. return str;
  144. }
  145. while (system_string_startswith(str, flag)) {
  146. str = str.substr(flag.length);
  147. }
  148. while (system_string_endswith(str, flag)) {
  149. str = str.substr(0, str.length - flag.length);
  150. }
  151. return str;
  152. }
  153. /**
  154. * array sort asc, for example:
  155. * [a, b] in [10, 11, 9]
  156. * then sort to: [9, 10, 11]
  157. * Usage, for example:
  158. obj.data.data.sort(function(a, b){
  159. return array_sort_asc(a.metadata.meta_id, b.metadata.meta_id);
  160. });
  161. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  162. * @remark, if need desc, use -1*array_sort_asc(a,b)
  163. */
  164. function array_sort_asc(elem_a, elem_b) {
  165. if (elem_a > elem_b) {
  166. return 1;
  167. }
  168. return (elem_a < elem_b)? -1 : 0;
  169. }
  170. function array_sort_desc(elem_a, elem_b) {
  171. return -1 * array_sort_asc(elem_a, elem_b);
  172. }
  173. function system_array_sort_asc(elem_a, elem_b) {
  174. return array_sort_asc(elem_a, elem_b);
  175. }
  176. function system_array_sort_desc(elem_a, elem_b) {
  177. return -1 * array_sort_asc(elem_a, elem_b);
  178. }
  179. /**
  180. * parse the query string to object.
  181. * parse the url location object as: host(hostname:http_port), pathname(dir/filename)
  182. * for example, url http://192.168.1.168:1980/ui/players.html?vhost=player.vhost.com&app=test&stream=livestream
  183. * parsed to object:
  184. {
  185. host : "192.168.1.168:1980",
  186. hostname : "192.168.1.168",
  187. http_port : 1980,
  188. pathname : "/ui/players.html",
  189. dir : "/ui",
  190. filename : "/players.html",
  191. vhost : "player.vhost.com",
  192. app : "test",
  193. stream : "livestream"
  194. }
  195. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  196. */
  197. function parse_query_string(){
  198. var obj = {};
  199. // add the uri object.
  200. // parse the host(hostname:http_port), pathname(dir/filename)
  201. obj.host = window.location.host;
  202. obj.hostname = window.location.hostname;
  203. obj.http_port = (window.location.port == "")? 80:window.location.port;
  204. obj.pathname = window.location.pathname;
  205. if (obj.pathname.lastIndexOf("/") <= 0) {
  206. obj.dir = "/";
  207. obj.filename = "";
  208. } else {
  209. obj.dir = obj.pathname.substr(0, obj.pathname.lastIndexOf("/"));
  210. obj.filename = obj.pathname.substr(obj.pathname.lastIndexOf("/"));
  211. }
  212. // pure user query object.
  213. obj.user_query = {};
  214. // parse the query string.
  215. var query_string = String(window.location.search).replace(" ", "").split("?")[1];
  216. if(query_string === undefined){
  217. query_string = String(window.location.hash).replace(" ", "").split("#")[1];
  218. if(query_string === undefined){
  219. return obj;
  220. }
  221. }
  222. __fill_query(query_string, obj);
  223. return obj;
  224. }
  225. function __fill_query(query_string, obj) {
  226. // pure user query object.
  227. obj.user_query = {};
  228. if (query_string.length === 0) {
  229. return;
  230. }
  231. // split again for angularjs.
  232. if (query_string.indexOf("?") >= 0) {
  233. query_string = query_string.split("?")[1];
  234. }
  235. var queries = query_string.split("&");
  236. for (var i = 0; i < queries.length; i++) {
  237. var elem = queries[i];
  238. var query = elem.split("=");
  239. obj[query[0]] = query[1];
  240. obj.user_query[query[0]] = query[1];
  241. }
  242. // alias domain for vhost.
  243. if (obj.domain) {
  244. obj.vhost = obj.domain;
  245. }
  246. }
  247. /**
  248. * parse the rtmp url,
  249. * for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
  250. * @return object {server, port, vhost, app, stream}
  251. * for exmaple, rtmp_url is rtmp://demo.srs.com:1935/live...vhost...players/livestream
  252. * parsed to object:
  253. {
  254. server: "demo.srs.com",
  255. port: 1935,
  256. vhost: "players",
  257. app: "live",
  258. stream: "livestream"
  259. }
  260. */
  261. function parse_rtmp_url(rtmp_url) {
  262. // @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
  263. var a = document.createElement("a");
  264. a.href = rtmp_url.replace("rtmp://", "http://")
  265. .replace("webrtc://", "http://")
  266. .replace("rtc://", "http://");
  267. var vhost = a.hostname;
  268. var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
  269. var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
  270. // parse the vhost in the params of app, that srs supports.
  271. app = app.replace("...vhost...", "?vhost=");
  272. if (app.indexOf("?") >= 0) {
  273. var params = app.substr(app.indexOf("?"));
  274. app = app.substr(0, app.indexOf("?"));
  275. if (params.indexOf("vhost=") > 0) {
  276. vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
  277. if (vhost.indexOf("&") > 0) {
  278. vhost = vhost.substr(0, vhost.indexOf("&"));
  279. }
  280. }
  281. }
  282. // when vhost equals to server, and server is ip,
  283. // the vhost is __defaultVhost__
  284. if (a.hostname === vhost) {
  285. var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
  286. if (re.test(a.hostname)) {
  287. vhost = "__defaultVhost__";
  288. }
  289. }
  290. // parse the schema
  291. var schema = "rtmp";
  292. if (rtmp_url.indexOf("://") > 0) {
  293. schema = rtmp_url.substr(0, rtmp_url.indexOf("://"));
  294. }
  295. var port = a.port;
  296. if (!port) {
  297. if (schema === 'http') {
  298. port = 80;
  299. } else if (schema === 'https') {
  300. port = 443;
  301. } else if (schema === 'rtmp') {
  302. port = 1935;
  303. }
  304. }
  305. var ret = {
  306. url: rtmp_url,
  307. schema: schema,
  308. server: a.hostname, port: port,
  309. vhost: vhost, app: app, stream: stream
  310. };
  311. __fill_query(a.search, ret);
  312. // For webrtc API, we use 443 if page is https, or schema specified it.
  313. if (!ret.port) {
  314. if (schema === 'webrtc' || schema === 'rtc') {
  315. if (ret.user_query.schema === 'https') {
  316. ret.port = 443;
  317. } else if (window.location.href.indexOf('https://') === 0) {
  318. ret.port = 443;
  319. } else {
  320. // For WebRTC, SRS use 1985 as default API port.
  321. ret.port = 1985;
  322. }
  323. }
  324. }
  325. return ret;
  326. }
  327. /**
  328. * get the agent.
  329. * @return an object specifies some browser.
  330. * for example, get_browser_agents().MSIE
  331. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  332. */
  333. function get_browser_agents() {
  334. var agent = navigator.userAgent;
  335. /**
  336. WindowsPC platform, Win7:
  337. chrome 31.0.1650.63:
  338. Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
  339. (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
  340. firefox 23.0.1:
  341. Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101
  342. Firefox/23.0
  343. safari 5.1.7(7534.57.2):
  344. Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2
  345. (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2
  346. opera 15.0.1147.153:
  347. Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
  348. (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
  349. OPR/15.0.1147.153
  350. 360 6.2.1.272:
  351. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
  352. Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
  353. .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
  354. .NET4.0E)
  355. IE 10.0.9200.16750(update: 10.0.12):
  356. Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
  357. Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
  358. .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
  359. .NET4.0E)
  360. */
  361. return {
  362. // platform
  363. Android: agent.indexOf("Android") != -1,
  364. Windows: agent.indexOf("Windows") != -1,
  365. iPhone: agent.indexOf("iPhone") != -1,
  366. // Windows Browsers
  367. Chrome: agent.indexOf("Chrome") != -1,
  368. Firefox: agent.indexOf("Firefox") != -1,
  369. QQBrowser: agent.indexOf("QQBrowser") != -1,
  370. MSIE: agent.indexOf("MSIE") != -1,
  371. // Android Browsers
  372. Opera: agent.indexOf("Presto") != -1,
  373. MQQBrowser: agent.indexOf("MQQBrowser") != -1
  374. };
  375. }
  376. /**
  377. * format relative seconds to HH:MM:SS,
  378. * for example, 210s formated to 00:03:30
  379. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  380. * @usage relative_seconds_to_HHMMSS(210)
  381. */
  382. function relative_seconds_to_HHMMSS(seconds){
  383. var date = new Date();
  384. date.setTime(Number(seconds) * 1000);
  385. var ret = padding(date.getUTCHours(), 2, '0')
  386. + ":" + padding(date.getUTCMinutes(), 2, '0')
  387. + ":" + padding(date.getUTCSeconds(), 2, '0');
  388. return ret;
  389. }
  390. /**
  391. * format absolute seconds to HH:MM:SS,
  392. * for example, 1389146480s (2014-01-08 10:01:20 GMT+0800) formated to 10:01:20
  393. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  394. * @usage absolute_seconds_to_HHMMSS(new Date().getTime() / 1000)
  395. */
  396. function absolute_seconds_to_HHMMSS(seconds){
  397. var date = new Date();
  398. date.setTime(Number(seconds) * 1000);
  399. var ret = padding(date.getHours(), 2, '0')
  400. + ":" + padding(date.getMinutes(), 2, '0')
  401. + ":" + padding(date.getSeconds(), 2, '0');
  402. return ret;
  403. }
  404. /**
  405. * format absolute seconds to YYYY-mm-dd,
  406. * for example, 1389146480s (2014-01-08 10:01:20 GMT+0800) formated to 2014-01-08
  407. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  408. * @usage absolute_seconds_to_YYYYmmdd(new Date().getTime() / 1000)
  409. */
  410. function absolute_seconds_to_YYYYmmdd(seconds) {
  411. var date = new Date();
  412. date.setTime(Number(seconds) * 1000);
  413. var ret = date.getFullYear()
  414. + "-" + padding(date.getMonth() + 1, 2, '0')
  415. + "-" + padding(date.getDate(), 2, '0');
  416. return ret;
  417. }
  418. /**
  419. * parse the date in str to Date object.
  420. * @param str the date in str, format as "YYYY-mm-dd", for example, 2014-12-11
  421. * @returns a date object.
  422. * @usage YYYYmmdd_parse("2014-12-11")
  423. */
  424. function YYYYmmdd_parse(str) {
  425. var date = new Date();
  426. date.setTime(Date.parse(str));
  427. return date;
  428. }
  429. /**
  430. * async refresh function call. to avoid multiple call.
  431. * @remark AsyncRefresh is for jquery to refresh the speicified pfn in a page;
  432. * if angularjs, use AsyncRefresh2 to change pfn, cancel previous request for angularjs use singleton object.
  433. * @param refresh_interval the default refresh interval ms.
  434. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  435. * the pfn can be implements as following:
  436. var async_refresh = new AsyncRefresh(pfn, 3000);
  437. function pfn() {
  438. if (!async_refresh.refresh_is_enabled()) {
  439. async_refresh.request(100);
  440. return;
  441. }
  442. $.ajax({
  443. type: 'GET', async: true, url: 'xxxxx',
  444. complete: function(){
  445. if (!async_refresh.refresh_is_enabled()) {
  446. async_refresh.request(0);
  447. } else {
  448. async_refresh.request(async_refresh.refresh_interval);
  449. }
  450. },
  451. success: function(res){
  452. // if donot allow refresh, directly return.
  453. if (!async_refresh.refresh_is_enabled()) {
  454. return;
  455. }
  456. // render the res.
  457. }
  458. });
  459. }
  460. */
  461. function AsyncRefresh(pfn, refresh_interval) {
  462. this.refresh_interval = refresh_interval;
  463. this.__handler = null;
  464. this.__pfn = pfn;
  465. this.__enabled = true;
  466. }
  467. /**
  468. * disable the refresher, the pfn must check the refresh state.
  469. */
  470. AsyncRefresh.prototype.refresh_disable = function() {
  471. this.__enabled = false;
  472. }
  473. AsyncRefresh.prototype.refresh_enable = function() {
  474. this.__enabled = true;
  475. }
  476. AsyncRefresh.prototype.refresh_is_enabled = function() {
  477. return this.__enabled;
  478. }
  479. /**
  480. * start new async request
  481. * @param timeout the timeout in ms.
  482. * user can use the refresh_interval of the AsyncRefresh object,
  483. * which initialized in constructor.
  484. */
  485. AsyncRefresh.prototype.request = function(timeout) {
  486. if (this.__handler) {
  487. clearTimeout(this.__handler);
  488. }
  489. this.__handler = setTimeout(this.__pfn, timeout);
  490. }
  491. /**
  492. * async refresh v2, support cancellable refresh, and change the refresh pfn.
  493. * @remakr for angularjs. if user only need jquery, maybe AsyncRefresh is better.
  494. * @see: http://blog.csdn.net/win_lin/article/details/17994347
  495. * Usage:
  496. bsmControllers.controller('CServers', ['$scope', 'MServer', function($scope, MServer){
  497. async_refresh2.refresh_change(function(){
  498. // 获取服务器列表
  499. MServer.servers_load({}, function(data){
  500. $scope.servers = data.data.servers;
  501. async_refresh2.request();
  502. });
  503. }, 3000);
  504. async_refresh2.request(0);
  505. }]);
  506. bsmControllers.controller('CStreams', ['$scope', 'MStream', function($scope, MStream){
  507. async_refresh2.refresh_change(function(){
  508. // 获取流列表
  509. MStream.streams_load({}, function(data){
  510. $scope.streams = data.data.streams;
  511. async_refresh2.request();
  512. });
  513. }, 3000);
  514. async_refresh2.request(0);
  515. }]);
  516. */
  517. function AsyncRefresh2() {
  518. /**
  519. * the function callback before call the pfn.
  520. * the protype is function():bool, which return true to invoke, false to abort the call.
  521. * null to ignore this callback.
  522. *
  523. * for example, user can abort the refresh by find the class popover:
  524. * async_refresh2.on_before_call_pfn = function() {
  525. * if ($(".popover").length > 0) {
  526. * async_refresh2.request();
  527. * return false;
  528. * }
  529. * return true;
  530. * };
  531. */
  532. this.on_before_call_pfn = null;
  533. // use a anonymous function to call, and check the enabled when actually invoke.
  534. this.__call = {
  535. pfn: null,
  536. timeout: 0,
  537. __enabled: false,
  538. __handler: null
  539. };
  540. }
  541. // singleton
  542. var async_refresh2 = new AsyncRefresh2();
  543. /**
  544. * initialize or refresh change. cancel previous request, setup new request.
  545. * @param pfn a function():void to request after timeout. null to disable refresher.
  546. * @param timeout the timeout in ms, to call pfn. null to disable refresher.
  547. */
  548. AsyncRefresh2.prototype.initialize = function(pfn, timeout) {
  549. this.refresh_change(pfn, timeout);
  550. }
  551. /**
  552. * stop refresh, the refresh pfn is set to null.
  553. */
  554. AsyncRefresh2.prototype.stop = function() {
  555. this.__call.__enabled = false;
  556. }
  557. /**
  558. * restart refresh, use previous config.
  559. */
  560. AsyncRefresh2.prototype.restart = function() {
  561. this.__call.__enabled = true;
  562. this.request(0);
  563. }
  564. /**
  565. * change refresh pfn, the old pfn will set to disabled.
  566. */
  567. AsyncRefresh2.prototype.refresh_change = function(pfn, timeout) {
  568. // cancel the previous call.
  569. if (this.__call.__handler) {
  570. clearTimeout(this.__handler);
  571. }
  572. this.__call.__enabled = false;
  573. // setup new call.
  574. this.__call = {
  575. pfn: pfn,
  576. timeout: timeout,
  577. __enabled: true,
  578. __handler: null
  579. };
  580. }
  581. /**
  582. * start new request, we never auto start the request,
  583. * user must start new request when previous completed.
  584. * @param timeout [optional] if not specified, use the timeout in initialize or refresh_change.
  585. */
  586. AsyncRefresh2.prototype.request = function(timeout) {
  587. var self = this;
  588. var this_call = this.__call;
  589. // clear previous timeout.
  590. if (this_call.__handler) {
  591. clearTimeout(this_call.__handler);
  592. }
  593. // override the timeout
  594. if (timeout == undefined) {
  595. timeout = this_call.timeout;
  596. }
  597. // if user disabled refresher.
  598. if (this_call.pfn == null || timeout == null) {
  599. return;
  600. }
  601. this_call.__handler = setTimeout(function(){
  602. // cancelled by refresh_change, ignore.
  603. if (!this_call.__enabled) {
  604. return;
  605. }
  606. // callback if the handler installled.
  607. if (self.on_before_call_pfn) {
  608. if (!self.on_before_call_pfn()) {
  609. return;
  610. }
  611. }
  612. // do the actual call.
  613. this_call.pfn();
  614. }, timeout);
  615. }