PageRenderTime 54ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/source/js/OnSIP-ContentPage.js

https://github.com/simple-beck/OnSIP-Click-toCall-for-Chrome
JavaScript | 465 lines | 323 code | 95 blank | 47 comment | 65 complexity | 480a4deb4e8adcc8728a7f2bb2da8c5f MD5 | raw file
  1. // Global vars
  2. var toDomain;
  3. var fromAddress;
  4. var enabled = false;
  5. var callIsInProgress = false;
  6. // Add listener for commands from the background process
  7. chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
  8. alterDOM(request);
  9. updateAddresses(request);
  10. // On callComplete
  11. if ( request.callSetupCompleted ) {
  12. dbg.log('APP :: callIsInProgress FALSE');
  13. callIsInProgress = false;
  14. ///$('.onsip-message-box').remove();
  15. }
  16. // On callError
  17. if ( request.callError ) {
  18. showErrorMessage(request.errorMsg, request.fromAddressError);
  19. }
  20. // On Setting error
  21. if ( request.settingsError ) {
  22. dbg.log('CONTENT :: setup extension first');
  23. dbg.log('APP :: callIsInProgress FALSE');
  24. callIsInProgress = false;
  25. showErrorMsg(request.errorMsg);
  26. }
  27. // On incoming call
  28. if ( request.incomingCall ){
  29. dbg.log(' CONTENT :: Request Incoming Call ');
  30. showSetupMessage(request.incomingNumber, request.incomingName, request.incomingCallTitle);
  31. }
  32. // On outgoing call
  33. if(request.outgoingCall ){
  34. dbg.log(' CONTENT :: Request Outgoing Call ');
  35. showSetupMessage(request.outgoingNumber, request.outgoingName, ' Calling ');
  36. }
  37. if ( request.incomingCallRetract ) {
  38. dbg.log(' CONTENT :: Call Retract ---->>>>> ');
  39. // setTimeout(function() {$('.onsip-message-box').remove();callIsInProgress = false;}, 4500);
  40. $('.onsip-message-box').remove();
  41. dbg.log('APP :: callIsInProgress FALSE');
  42. callIsInProgress = false;
  43. }
  44. if ( request.incomingCallConfirmed ) {
  45. dbg.log(' CONTENT :: Request incomingCallConfirmed');
  46. dbg.log('APP :: callIsInProgress FALSE');
  47. callIsInProgress = false;
  48. ///$('.onsip-message-box').remove();
  49. }
  50. });
  51. // alter DOM on page load
  52. chrome.extension.sendRequest({pageLoad:true}, function (response) {
  53. alterDOM(response);
  54. updateAddresses(response);
  55. });
  56. // add listener for DOM changes, to parse the new nodes for phone numbers
  57. //document.addEventListener('DOMSubtreeModified', handleDomChange, true);
  58. document.addEventListener('DOMNodeInserted', handleDomChange, true);
  59. // stupid WebKit won't trigger DOMNodeInserted on innerHTML with plain text and no HTML tags
  60. document.addEventListener('DOMCharacterDataModified', handleDomChange, true);
  61. // prevent it from going into an infinite loop
  62. var parsing = false;
  63. function handleDomChange(e) {
  64. //return ;
  65. if (enabled) {
  66. if (parsing) return;
  67. var newNodeClass = e.srcElement.className;
  68. if ( newNodeClass != undefined ) {
  69. if (/onsip\-message\-box/.test(newNodeClass) || newNodeClass == 'onsip-click-to-call-icon') {
  70. return;
  71. }
  72. }
  73. var targetNode = (e.relatedNode) ? e.relatedNode : e.target;
  74. parsing = true;
  75. setTimeout(function(){
  76. parseDOM(targetNode);
  77. parsing = false;
  78. }, 10);
  79. }
  80. }
  81. // alter the DOM
  82. function alterDOM(request) {
  83. // parse DOM command
  84. if ( request.parseDOM ) {
  85. enabled = true;
  86. parseDOM(document.body);
  87. } else if ( request.clearDOM ) {
  88. // clear DOM command
  89. enabled = false;
  90. clearDOM();
  91. }
  92. }
  93. function updateAddresses(request) {
  94. if (request.fromAddress) {
  95. fromAddress = request.fromAddress;
  96. toDomain = getDomain(fromAddress);
  97. // update toAddress in link href
  98. $('.onsip-click-to-call').each(function(){
  99. var href = this.href;
  100. href = href.substr(0, href.indexOf('@') + 1);
  101. href += toDomain;
  102. this.href = href;
  103. });
  104. }
  105. }
  106. // remove click-to-call links and messages
  107. function clearDOM() {
  108. $('.onsip-message-box').remove();
  109. $('.onsip-click-to-call').each(function(){
  110. $(this).replaceWith(this.innerHTML);
  111. });
  112. }
  113. // parse DOM and convert phone numbers to click-to-call links
  114. function parseDOM(node) {
  115. var invalidNodes = ['SCRIPT', 'STYLE', 'INPUT', 'SELECT', 'TEXTAREA', 'BUTTON', 'A', 'CODE'];
  116. var nodeName = node.nodeName.toUpperCase();
  117. var childNodesLength = node.childNodes.length;
  118. if ( $.inArray(nodeName, invalidNodes) > -1 || $(node).hasClass('onsip-message-box') ) {
  119. return 0;
  120. }
  121. for (var n = 0; n < childNodesLength; n++) {
  122. var found = parseDOM(node.childNodes[n]);
  123. if ( found > 0 ) {
  124. parseDOM(node);
  125. return 0;
  126. }
  127. }
  128. if (node.nodeType == Node.TEXT_NODE) {
  129. return parsePhoneNumbers(node);
  130. } else {
  131. addEvents(node);
  132. }
  133. return 0;
  134. }
  135. // replace phone numbers
  136. function parsePhoneNumbers(node) {
  137. var isStringNumber = false;
  138. // SIP adress
  139. var sipAddressNumber = /sip:[a-zA-Z09_]+@[a-zA-Z09_]+\.[a-z]{1,4}/;
  140. // Eliminate the obvious cases
  141. if (!node || node.nodeValue.length < 10 ||
  142. node.nodeValue.search(/\d/) == -1 && node.nodeValue.match(sipAddressNumber) == null) {
  143. return 0;
  144. }
  145. var phoneNumber = /((((\+|(00))[1-9]\d{0,3}[\s\-.]?)?\d{2,4}[\s\/\-.]?)|21)\d{5,9}/;
  146. var phoneNumberNorthAmerica = /\+?(1[\s-.])?((\(\d{3}\))|(\d{3}))[\s.\-]\d{3}[\s.\-]\d{4}/;
  147. // phone number with an extension
  148. var phoneNumberNorthAmericaWithExtension = /\+?(1[\s-.])?((\(\d{3}\))|(\d{3}))[\s.\-]\d{3}[\s.\-]\d{4}\s{1,5}(ext|x|ex)\s{0,3}.{0,3}\d{2,5}/;
  149. var phoneNumberExtension = /(ext|x|ex)\s{0,3}.{0,3}\d{2,5}/g;
  150. var phoneNumberDelimiter = /[\s.,;:|]/;
  151. var text = node.nodeValue;
  152. var offset = 0;
  153. var number = "";
  154. // extension
  155. var extension = null;
  156. var found = false;
  157. var foundNorthAmerica = false;
  158. // find the first phone number in the text node
  159. while (!found) {
  160. var result = text.match(phoneNumberNorthAmerica);
  161. // handling extension
  162. var resultWithExtension = text.match(phoneNumberNorthAmericaWithExtension);
  163. if(resultWithExtension){
  164. extension = text.match(phoneNumberExtension);
  165. extension = extension[0];
  166. }
  167. if(result == null){
  168. result = text.match(sipAddressNumber);
  169. if(result != null){
  170. isStringNumber = true;
  171. }
  172. }
  173. if (result) {
  174. foundNorthAmerica = true;
  175. }
  176. else {
  177. foundNorthAmerica = false;
  178. }
  179. if (!result) {
  180. return 0;
  181. }
  182. number = result[0];
  183. if(!isStringNumber){
  184. var pos = result.index;
  185. offset += pos;
  186. // make sure we have a resonable delimiters around our matching number
  187. if (pos && !text.substr(pos - 1, 1).match(phoneNumberDelimiter)
  188. || pos + number.length < text.length
  189. && !text.substr(pos + number.length, 1).match(phoneNumberDelimiter)) {
  190. offset += number.length;
  191. text = text.substr(pos + number.length);
  192. continue;
  193. }
  194. }else{
  195. var pos = result.index;
  196. offset += pos;
  197. }
  198. // looks like we found a phone number
  199. found = true;
  200. }
  201. // handle string address
  202. if(isStringNumber){
  203. var stringNumber = number.replace(/sip:/,'');
  204. var spanNode = $('<a href="onsip:' + stringNumber + '" title="Click-to-Call ' + stringNumber + '" class="onsip-click-to-call" rel="' + stringNumber + '" extension="' +extension +'"></a>')[0];
  205. }else{
  206. // wrap the phone number in a span tag
  207. var cleanNumber = cleanPhoneNo(number);
  208. if (foundNorthAmerica && cleanNumber.length == 10) {
  209. cleanNumber = "1" + cleanNumber;
  210. }
  211. var spanNode = $('<a href="onsip:' + cleanNumber + '@' + toDomain + '" title="Click-to-Call ' + number + '" class="onsip-click-to-call" rel="' + cleanNumber + '" extension="' +extension +'"></a>')[0];
  212. }
  213. var range = node.ownerDocument.createRange();
  214. range.setStart(node, offset);
  215. range.setEnd(node, offset + number.length);
  216. var docfrag = range.extractContents();
  217. var before = range.startContainer.splitText(range.startOffset);
  218. var parent = before.parentNode;
  219. spanNode.appendChild(docfrag);
  220. parent.insertBefore(spanNode, before);
  221. return 1;
  222. }
  223. function addEvents(node) {
  224. $('.onsip-click-to-call', node)
  225. .unbind()
  226. .bind({
  227. click : function(e){
  228. e.preventDefault();
  229. callNumber(this.innerHTML, this.rel, $(this).attr('extension'));
  230. },
  231. mouseover : function() {
  232. var $this = $(this);
  233. var offset = $this.offset();
  234. var top = offset.top - 20;
  235. top = (top > 0) ? top : 0;
  236. var left = offset.left - 18;
  237. left = (left > 0) ? left : 0;
  238. var icon = $('<div class="onsip-click-to-call-icon"></div>');
  239. iconFile = chrome.extension.getURL('images/icon-phone.png');
  240. icon.css({
  241. 'background-image' : 'url(' + iconFile + ')',
  242. 'top' : top + 'px',
  243. 'left' : left + 'px'
  244. })
  245. .appendTo('body')
  246. .fadeIn(200);
  247. $this.data('icon', icon);
  248. },
  249. mouseout : function() {
  250. var $this = $(this);
  251. $this.data('icon')
  252. .fadeOut(200, function() {
  253. $(this).remove();
  254. });
  255. }
  256. });
  257. }
  258. /**
  259. * Call the given number
  260. * @param phoneNo
  261. * @param cleanNo
  262. */
  263. function callNumber(phoneNo, cleanNo, extension) {
  264. dbg.log('CONTENT :: call number signal');
  265. if(!callIsInProgress){
  266. dbg.log('CONTENT :: call IS NOT inprogress');
  267. // all pages will try to setup a phone call through the background process
  268. chrome.extension.sendRequest({setupCall : true, phoneNo : cleanNo, extension : extension}, function (response) {
  269. if(!response.popupDisabled){
  270. if ( response.callInProgress ) {
  271. showBusyMessage();
  272. } else {
  273. showSetupMessage(phoneNo, response.customerName, 'Calling ');
  274. }
  275. }
  276. });
  277. }
  278. dbg.log('CONTENT :: call IS inprogress');
  279. callIsInProgress = true;
  280. }
  281. /**
  282. * Show setup message
  283. */
  284. function showSetupMessage(phoneNo, customerName, msg) {
  285. dbg.log('CONTENT :: setupMessage ');
  286. // take care of null name
  287. if(customerName == null){
  288. customerName = '';
  289. }
  290. if(msg == null){
  291. msg = '';
  292. }
  293. var call = $('.onsip-call-message');
  294. if (call.size() == 0) {
  295. $('.onsip-message-box').remove();
  296. call = $('<div class="onsip-message-box onsip-call-message" style="background: transparent url( '+ chrome.extension.getURL('images/pop_up_background.png') + ' ) no-repeat "><div id="onsip-message-wrapper" > <div id="popup-message">'+ msg +' </div> <div id="popup-name">'+ customerName + ' </div> <div id="popup-number">' + phoneNo + ' </div> <p id="close-btn-wrapper" ><a id="close-notification-btn" href="#" > <img src="'+ chrome.extension.getURL('images/pop_up_close.png') + '" /></a><p></div></div>');
  297. call.find('A').click(function(e){
  298. e.preventDefault();
  299. dbg.log('APP :: callIsInProgress FALSE');
  300. callIsInProgress = false;
  301. chrome.extension.sendRequest({setupCallCancel : true}, function (response) {});
  302. call.remove();
  303. });
  304. $('body').append(call);
  305. }
  306. }
  307. function showBusyMessage() {
  308. var msg = $('.onsip-busy-message');
  309. var call = $('.onsip-call-message');
  310. if (msg.size() == 0 && call.size() == 0) {
  311. $('.onsip-message-box').remove();
  312. msg = $('<div class="onsip-message-box onsip-busy-message"><div>Another phone call request is already in progress!</div></div>');
  313. msg.find('DIV').css('background-image', 'url(' + chrome.extension.getURL('images/i_warning.png') + ')');
  314. $('body').append(msg);
  315. addMessageCloseEvent(msg);
  316. }
  317. delayedClose(msg);
  318. }
  319. function showErrorMessage(msg, fromAddressError) {
  320. $('.onsip-message-box').remove();
  321. var DIV = '<div class="onsip-message-box onsip-call-message" style="background: transparent url( '+ chrome.extension.getURL('images/pop_up_background.png') + ' ) no-repeat "><div id="onsip-message-wrapper" > ' + msg + '<p>';
  322. if (fromAddressError) {
  323. DIV += '<a href="#" class="onsip-edit-address">Edit telephone number address</a> ';
  324. }
  325. DIV += '<p id="close-btn-wrapper" ><a href="#" > <img src="'+ chrome.extension.getURL('images/pop_up_close.png') + '" /></a><p><p></div></div>';
  326. error = $(DIV);
  327. error.find('DIV').css('background', 'url(' + chrome.extension.getURL('images/i_error.png') + ') no-repeat');
  328. error.find('A').click(function(e){e.preventDefault();});
  329. $('body').append(error);
  330. addMessageCloseEvent(error);
  331. $('.onsip-edit-address').click(function(e){
  332. e.preventDefault();
  333. chrome.extension.sendRequest({openSettingsPage : true}, function (response) {});
  334. });
  335. }
  336. /**
  337. * Show error message
  338. */
  339. function showErrorMsg(msg){
  340. dbg.log('CONTENT :: showErrorMsg');
  341. // take care of null name
  342. $('.onsip-message-box').remove();
  343. var DIV = '<div class="onsip-message-box onsip-call-message" style="background: transparent url( '+ chrome.extension.getURL('images/pop_up_background.png') + ' ) no-repeat "><div id="onsip-message-wrapper" > ' + msg + '<p>';
  344. error = $(DIV);
  345. error.find('DIV').css('background', 'url(' + chrome.extension.getURL('images/i_error.png') + ') no-repeat');
  346. error.find('A').click(function(e){e.preventDefault();});
  347. $('body').append(error);
  348. addMessageCloseEvent(error);
  349. error.click(function(e){
  350. e.preventDefault();
  351. chrome.extension.sendRequest({openSettingsPage : true}, function (response) {});
  352. });
  353. }
  354. function addMessageCloseEvent(el) {
  355. el.click(function(){
  356. el.fadeOut(300, function(){
  357. el.remove()
  358. });
  359. });
  360. $(document).keyup(function(e){
  361. if (e.keyCode == 27) el.click();
  362. });
  363. }
  364. function delayedClose(el) {
  365. clearTimeout(el.data('to'));
  366. var to = setTimeout(function(){
  367. el.click();
  368. }, 5000);
  369. el.data('to', to);
  370. }