utils.js


SUBMITTED BY: Guest

DATE: July 9, 2021, 2:39 p.m.

FORMAT: JavaScript

SIZE: 23.9 kB

HITS: 494

  1. define('core/utils',[
  2. 'jquery',
  3. 'underscore',
  4. 'core/config',
  5. 'core/utils/browser',
  6. 'core/utils/isMobileUserAgent',
  7. ], function (
  8. $,
  9. _,
  10. config,
  11. browser,
  12. isMobileUserAgent
  13. ) {
  14. 'use strict';
  15. var doc = window.document;
  16. // From https://github.com/ryanseddon/H5F/blob/039588f4b1d8513b09a49f903d70ff269435d4b3/src/H5F.js#L20
  17. var emailPatt = /^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
  18. var validateEmail = function (email) {
  19. return emailPatt.test(email);
  20. };
  21. /*
  22. * This uses the same url RegExp as Bleach version 1.0.3 and returns
  23. * the links it finds. This is not a direct match to Bleach, as there is no
  24. * true HTML parsing, but it is sufficient for finding urls.
  25. */
  26. // TODO: config.TLDS is included to support an Embed override. Embed gets
  27. // an up-to-date list every build via manifest.yaml and the interpolator;
  28. // an equivalent system should be incorporated in next-core
  29. var TLDS = config.TLDS ||
  30. 'zw|zuerich|zone|zm|zip|za|yt|youtube|yokohama|yoga|yodobashi|ye|' +
  31. 'yandex|yachts|xyz|xxx|xin|xerox|wtf|wtc|ws|world|works|work|wme|win|' +
  32. 'williamhill|wiki|wien|whoswho|wf|weir|wedding|wed|website|webcam|' +
  33. 'watch|wang|wales|vu|voyage|voto|voting|vote|vodka|vn|vlaanderen|' +
  34. 'vision|villas|video|viajes|vi|vg|vet|versicherung|ventures|vegas|ve|' +
  35. 'vc|vacations|va|uz|uy|us|uol|uno|university|uk|ug|ua|tz|tw|tv|tui|' +
  36. 'tt|trust|travel|training|trading|trade|tr|toys|town|tours|toshiba|' +
  37. 'toray|top|tools|tokyo|today|to|tn|tm|tl|tk|tj|tirol|tires|tips|' +
  38. 'tienda|tickets|theater|th|tg|tf|tennis|temasek|tel|technology|tech|' +
  39. 'team|td|tc|taxi|tax|tattoo|tatar|taipei|sz|systems|sydney|sy|sx|' +
  40. 'swiss|sv|suzuki|surgery|surf|support|supply|supplies|sucks|su|style|' +
  41. 'study|st|sr|spreadbetting|spiegel|space|soy|sony|solutions|solar|' +
  42. 'sohu|software|social|so|sn|sm|sl|sky|sk|sj|site|singles|si|shriram|' +
  43. 'show|shoes|shiksha|sh|sg|sexy|sex|sew|services|sener|seat|se|sd|' +
  44. 'scot|science|schwarz|schule|school|scholarships|schmidt|scb|sca|sc|' +
  45. 'sb|saxo|sarl|sap|samsung|sale|saarland|sa|ryukyu|rw|run|ruhr|ru|' +
  46. 'rsvp|rs|rodeo|rocks|ro|rip|rio|rich|reviews|review|restaurant|rest|' +
  47. 'republican|report|repair|rentals|rent|ren|reit|reisen|reise|rehab|' +
  48. 'redstone|red|recipes|realtor|re|racing|quebec|qpon|qa|py|pw|pub|pt|' +
  49. 'ps|property|properties|prof|productions|prod|pro|press|praxi|pr|' +
  50. 'post|porn|poker|pohl|pn|pm|plus|plumbing|place|pl|pk|pizza|pink|' +
  51. 'pictures|pictet|pics|piaget|physio|photos|photography|photo|philips|' +
  52. 'pharmacy|ph|pg|pf|pe|party|parts|partners|paris|panerai|page|pa|ovh|' +
  53. 'otsuka|osaka|organic|org|oracle|ooo|online|onl|ong|one|om|okinawa|' +
  54. 'nz|nyc|nu|ntt|nrw|nra|nr|np|no|nl|nissan|ninja|nico|ni|nhk|ngo|ng|' +
  55. 'nf|nexus|news|new|neustar|network|net|nec|ne|nc|navy|name|nagoya|' +
  56. 'nadex|na|mz|my|mx|mw|mv|museum|mu|mtpc|mtn|mt|ms|mr|mq|mp|movie|mov|' +
  57. 'motorcycles|moscow|mortgage|mormon|money|monash|moe|moda|mobi|mo|mn|' +
  58. 'mma|mm|ml|mk|mini|mil|miami|mh|mg|menu|memorial|meme|melbourne|meet|' +
  59. 'media|me|md|mc|marriott|markets|marketing|market|mango|management|' +
  60. 'maison|maif|madrid|ma|ly|lv|luxury|luxe|lu|ltda|lt|ls|lr|love|lotto|' +
  61. 'lotte|london|lol|loans|loan|lk|link|limo|limited|lighting|life|lidl|' +
  62. 'liaison|li|lgbt|legal|leclerc|lease|lds|lc|lb|lawyer|latrobe|lat|' +
  63. 'land|lacaixa|la|kz|kyoto|ky|kw|kred|krd|kr|kp|komatsu|koeln|kn|km|' +
  64. 'kiwi|kitchen|kim|ki|kh|kg|ke|kddi|kaufen|juegos|jp|joburg|jobs|jo|' +
  65. 'jm|jewelry|jetzt|je|jcb|java|iwc|it|is|irish|ir|iq|io|investments|' +
  66. 'international|int|insure|institute|ink|ing|info|infiniti|industries|' +
  67. 'in|immobilien|immo|im|il|ifm|ie|id|icu|ibm|hu|ht|hr|how|house|' +
  68. 'hosting|host|horse|honda|homes|holiday|holdings|hockey|hn|hm|hk|hiv|' +
  69. 'hitachi|hiphop|hermes|here|help|healthcare|haus|hangout|hamburg|gy|' +
  70. 'gw|guru|guitars|guide|guge|gu|gt|gs|gripe|green|gratis|graphics|gr|' +
  71. 'gq|gp|gov|gop|google|goog|goo|golf|goldpoint|gold|gn|gmx|gmo|gmail|' +
  72. 'gm|globo|global|gle|glass|gl|gives|gifts|gift|gi|gh|ggee|gg|gf|gent|' +
  73. 'ge|gdn|gd|gbiz|gb|garden|gallery|gal|ga|futbol|furniture|fund|' +
  74. 'frogans|frl|fr|foundation|forsale|forex|football|foo|fo|fm|fly|' +
  75. 'flsmidth|flowers|florist|flights|fk|fj|fitness|fit|fishing|fish|' +
  76. 'firmdale|financial|finance|film|fi|feedback|fashion|farm|fans|fan|' +
  77. 'faith|fail|express|exposed|expert|exchange|everbank|events|eus|' +
  78. 'eurovision|eu|et|estate|esq|es|erni|er|equipment|epson|enterprises|' +
  79. 'engineering|engineer|energy|emerck|email|eg|ee|education|edu|ec|eat|' +
  80. 'dz|dvag|durban|download|doosan|domains|doha|dog|docs|do|dnp|dm|dk|' +
  81. 'dj|discount|directory|direct|digital|diet|diamonds|dev|design|desi|' +
  82. 'dentist|dental|democrat|delivery|degree|deals|de|dclk|day|datsun|' +
  83. 'dating|date|dance|dad|dabur|cz|cyou|cymru|cy|cx|cw|cv|cuisinella|cu|' +
  84. 'cruises|crs|cricket|creditcard|credit|cr|courses|country|coop|cool|' +
  85. 'cooking|contractors|consulting|construction|condos|computer|company|' +
  86. 'community|com|cologne|college|coffee|codes|coach|co|cn|cm|club|' +
  87. 'clothing|clinic|click|cleaning|claims|cl|ck|city|citic|ci|church|' +
  88. 'chrome|christmas|chloe|cheap|chat|channel|ch|cg|cfd|cfa|cf|cern|ceo|' +
  89. 'center|cd|cc|cbn|catering|cat|casino|cash|casa|cartier|cars|careers|' +
  90. 'career|care|cards|caravan|capital|capetown|canon|cancerresearch|' +
  91. 'camp|camera|cal|cafe|cab|ca|bzh|bz|by|bw|bv|buzz|business|builders|' +
  92. 'build|budapest|bt|bs|brussels|brother|broker|bridgestone|br|' +
  93. 'boutique|boo|bond|boats|bo|bnpparibas|bn|bmw|bm|blue|bloomberg|' +
  94. 'blackfriday|black|bj|biz|bio|bingo|bike|bid|bi|bh|bg|bf|best|berlin|' +
  95. 'beer|be|bd|bbc|bb|bayern|bauhaus|bargains|barclays|barclaycard|bar|' +
  96. 'bank|band|ba|az|axa|ax|aw|autos|auto|audio|auction|au|attorney|at|' +
  97. 'associates|asia|as|arpa|army|archi|ar|aquarelle|aq|apartments|ao|' +
  98. 'android|an|amsterdam|am|alsace|allfinanz|al|airforce|aig|ai|agency|' +
  99. 'ag|afl|af|aero|ae|adult|ads|ad|actor|active|accountants|accountant|' +
  100. 'accenture|academy|ac|abogado|abbott|abb';
  101. // Url RegExp
  102. var URL_RE = new RegExp(
  103. // http://
  104. // Note: First group must be removed from match when
  105. // processing, in python it was a look-behind.
  106. '([^@.]|^)\\b(?:\\w[\\w-]*:/{0,3}(?:(?:\\w+:)?\\w+@)?)?' +
  107. // xx.yy.tld
  108. '([\\w-]+\\.)+(?:' + TLDS + ')(?!\\.\\w)\\b' +
  109. // optional port
  110. '(?::\\d+)?' +
  111. // /path/zz (excluding "unsafe" chars from RFC 1738,
  112. // except for # and ~, which happen in practice)
  113. '(?:[/?][^\\s\\{\\}\\|\\\\\\^\\[\\]`<>"\\x80-\\xFF\\x00-\\x1F\\x7F]*)?',
  114. // Flags.
  115. // Note: ignore case is NOT set because Bleach version 1.0.3 does not
  116. // set it. This is a bug, resulting in urls being ignored if the TLD
  117. // is capitalized. However, it is repeated here for consistency with
  118. // the server; if we update Bleach we should update this method as well.
  119. 'g'
  120. );
  121. var isUrl = function (urlStr) {
  122. return Boolean(urlStr.match(URL_RE));
  123. };
  124. // Protocol RegExp
  125. var PROTO_RE = /^[\w-]+:\/{0,3}/;
  126. // Punctuation RegExp.
  127. // This is global so that lastIndex is set.
  128. var PUNCT_RE = /([.,]+)$/g;
  129. var bleachFindUrls = function (message) {
  130. var urlRecord = [];
  131. var match, matchedStr, punctMatch, href, group1;
  132. if (!message) {
  133. return urlRecord;
  134. }
  135. while (match = URL_RE.exec(message)) { // eslint-disable-line no-cond-assign
  136. matchedStr = match[0];
  137. group1 = match[1];
  138. // Remove first group of match (see comment in URL_RE)
  139. matchedStr = matchedStr.slice(group1.length);
  140. // Remove trailing punctuation
  141. PUNCT_RE.lastIndex = 0;
  142. punctMatch = PUNCT_RE.exec(matchedStr);
  143. if (punctMatch) {
  144. matchedStr = matchedStr.slice(0, matchedStr.length - punctMatch[0].length);
  145. }
  146. // Normalize protocol
  147. if (PROTO_RE.test(matchedStr)) {
  148. href = matchedStr;
  149. } else {
  150. href = 'http://' + matchedStr;
  151. }
  152. // Store match
  153. var index = match.index + group1.length;
  154. urlRecord.push({
  155. text: matchedStr,
  156. url: href,
  157. index: index,
  158. endIndex: index + matchedStr.length,
  159. });
  160. }
  161. return urlRecord;
  162. };
  163. // Regex to match sequence of 1 or more punctuation
  164. // characters at the end of a string
  165. // See: www.unicode.org/charts/
  166. var terminatingPuncRe = new RegExp('[' +
  167. // Basic Latin (ASCII punctuation blocks)
  168. '\\u0021-\\u002F' +
  169. '\\u003A-\\u0040' +
  170. '\\u005B-\\u0060' +
  171. '\\u007B-\\u007E' +
  172. // Latin-1 Supplement (Latin-1 punctuation)
  173. '\\u00A1-\\u00BF' +
  174. // General Punctuation
  175. '\\u2010-\\u2027' +
  176. '\\u2030-\\u205E' +
  177. // Miscellaneous technical
  178. '\\u2300-\\u23FF' +
  179. // Supplemental punctuation
  180. '\\u2E00-\\u2E7F' +
  181. // CJK Symbols and Punctuation
  182. '\\u3001-\\u303F' +
  183. // Vertical forms
  184. '\\uFE10-\\uFE19' +
  185. // CJK Compatibility Forms
  186. '\\uFE30-\\uFE4F' +
  187. // Small form variants
  188. '\\uFE50-\\uFE6B' +
  189. // Fullwidth ASCII Forms
  190. '\\uFF01-\\uFF0F' +
  191. '\\uFF1A-\\uFF20' +
  192. '\\uFF3B-\\uFF40' +
  193. '\\uFF5B-\\uFF60' +
  194. // Halfwidth CJK Punctuation
  195. '\\uFF5F-\\uFF64' +
  196. ']+$');
  197. var MAX_TRUNCATION_RATIO = 0.5;
  198. // Given a character limit (upperLimit) return the whole text
  199. // if it fits within the limit, otherwise try to truncate
  200. // within that limit without chopping off a word halfway or
  201. // returning text with dangling punctuation but also without
  202. // truncating the text more than halfway.
  203. var niceTruncate = function (text, upperLimit) {
  204. if (text.length <= upperLimit) {
  205. return text;
  206. }
  207. text = text.slice(0, upperLimit - 1); // account the ellipsis char
  208. var max = text;
  209. // There's no way to know if the last sequence of chars is
  210. // a whole word or not, so scan the sliced text backwards
  211. // for the first whitespace character and truncate there
  212. // Greedily match everything up to the last chunk of whitespace
  213. var matches = /(^.*\S)\s/.exec(text);
  214. if (matches) {
  215. text = matches[1];
  216. }
  217. var endPunctuation = terminatingPuncRe.exec(text);
  218. // Remove any trailing punctuation
  219. if (endPunctuation) {
  220. text = text.slice(0, text.length - endPunctuation[0].length);
  221. }
  222. // If the further truncation above caused the text we're returning
  223. // to be less than 1/2 what it was before further truncation, we undo
  224. if (text.length < MAX_TRUNCATION_RATIO * max.length) {
  225. text = max;
  226. }
  227. return text + '\u2026'; // Ellipsis
  228. };
  229. var _hasStyleProperty = (function () {
  230. var el = doc.createElement('fakeelement');
  231. return function (_cssName, jsName) {
  232. return el.style[jsName] !== undefined;
  233. };
  234. }());
  235. // http://stackoverflow.com/questions/5023514/how-do-i-normalize-css3-transition-functions-across-browsers
  236. var transitionEndEvent = (function () {
  237. var transitions = {
  238. transition: 'transitionend',
  239. OTransition: 'otransitionend',
  240. MozTransition: 'transitionend',
  241. WebkitTransition: 'webkitTransitionEnd',
  242. };
  243. return _.find(transitions, _hasStyleProperty) || null;
  244. })();
  245. // https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Using_CSS_animations/Detecting_CSS_animation_support
  246. var animationEndEvent = (function () {
  247. var animations = {
  248. animation: 'animationend',
  249. OAnimation: 'oAnimationEnd',
  250. MozAnimation: 'animationend',
  251. WebkitAnimation: 'webkitAnimationEnd',
  252. };
  253. return _.find(animations, _hasStyleProperty) || null;
  254. })();
  255. /**
  256. * Utility helper for DOM event handlers that automatically calls preventDefault for you
  257. *
  258. * @param {function} handler The event handler to be wrapped/modified
  259. * @returns {function} The new, augmented event handler that prevents the default action for the event
  260. */
  261. function preventDefaultHandler(handler) {
  262. return function (evt) {
  263. if (evt && evt.preventDefault) {
  264. evt.preventDefault();
  265. }
  266. return handler.apply(this, arguments);
  267. };
  268. }
  269. /**
  270. * Utility helper for DOM event handlers that automatically calls stopPropagation for you
  271. *
  272. * @param {function} handler The event handler to be wrapped/modified
  273. * @returns {function} The new, augmented event handler that stops the propagation of the event
  274. */
  275. function stopPropagationHandler(handler) {
  276. return function (evt) {
  277. if (evt && evt.stopPropagation) {
  278. evt.stopPropagation();
  279. }
  280. return handler.apply(this, arguments);
  281. };
  282. }
  283. function stopEventHandler(handler) {
  284. return preventDefaultHandler(stopPropagationHandler(handler));
  285. }
  286. function getDomain(url) {
  287. // Extracts hostname from url, without the www prefix.
  288. if (!url) {
  289. return '';
  290. }
  291. // Force the protocol to http.
  292. // This resolves an issue where some browsers do not extract
  293. // the hostname if the url does not use a typical protocol.
  294. // Forcing a protocol is also required since if missing the
  295. // url will be resolved as a relative url on our domain.
  296. url = 'http://' + url.replace(/^([a-z+.-]+:)?\/+/i, '');
  297. var link = doc.createElement('a');
  298. link.href = url;
  299. // Remove www prefix and any trailing numbers.
  300. // Since getDomain is used only for display purposes this is a
  301. // safe operation.
  302. var domain = link.hostname.replace(/^www\d*\./i, '');
  303. // Normalize capitalization since browsers are inconsistent
  304. // about this.
  305. domain = domain.toLowerCase();
  306. return domain;
  307. }
  308. function serializeArgs(params) {
  309. var pcs = [];
  310. _.each(params, function (val, key) {
  311. if (val !== undefined) {
  312. pcs.push(key + (val === null ? '' : '=' + encodeURIComponent(val)));
  313. }
  314. });
  315. return pcs.join('&');
  316. }
  317. function serialize(url, params, nocache) {
  318. if (params) {
  319. if (url.indexOf('?') === -1) {
  320. url += '?';
  321. } else if (url.charAt(url.length - 1) !== '&') {
  322. url += '&';
  323. }
  324. url += this.serializeArgs(params);
  325. }
  326. if (nocache) {
  327. var ncp = {};
  328. ncp[(new Date()).getTime()] = null;
  329. return this.serialize(url, ncp);
  330. }
  331. var len = url.length;
  332. return url.charAt(len - 1) === '&' ? url.slice(0, len - 1) : url;
  333. }
  334. /**
  335. * window.open wrapper
  336. *
  337. * IMPORTANT: must obey same contract as window.open
  338. * i.e., same function signature, same return value
  339. *
  340. * We wrap it because it is not possible to stub `window.open`
  341. * reliably across browsers for testing, which causes issues from time to time.
  342. *
  343. * @param {string} url The URL for the new popup window
  344. * @param {string} [title] The title of the new popup window
  345. * @param {Object} [options] Options for the new window such as height and width. The following are overridden:
  346. * location, status, resizable, scrollbars for UX and cross-browser consistency.
  347. * @returns {window|null} The reference to the newly created popup window, or `null` if popup is blocked.
  348. */
  349. function openWindow(url, title, options) {
  350. // Don't add any defaults if options object is not provided. Not using
  351. // options is the only way to default to opening a new tab instead of a
  352. // window when using `window.open`
  353. if (options) {
  354. _.extend(options, {
  355. 'location': 1,
  356. 'status': 1,
  357. 'resizable': 1,
  358. 'scrollbars': 1,
  359. });
  360. } else {
  361. options = {};
  362. }
  363. if (options.width && options.height) {
  364. // By default, position window in the middle of the browser screen
  365. _.defaults(options, {
  366. left: window.screen.width / 2 - options.width / 2,
  367. top: window.screen.height / 2 - options.height / 2,
  368. });
  369. }
  370. var optionsStr = _.map(options, function (val, key) {
  371. return key + '=' + val;
  372. }).join(',');
  373. return window.open(url, title, optionsStr);
  374. }
  375. /**
  376. * Safely insert a string into another string at specified index.
  377. * Spaces will be guaranteed to surround the inserted string, except
  378. * at the beginning of the new string.
  379. *
  380. * @param {string} string The string to be inserted into
  381. * @param {number} index The position to insert into
  382. * @param {string} stringToInsert The string to be inserted
  383. *
  384. * @returns {string} The newly crafted string
  385. */
  386. function insertWithWhitespace(string, index, stringToInsert) {
  387. if (index < 0) {
  388. index = 0;
  389. }
  390. var before = string.substring(0, index);
  391. var after = string.substring(index);
  392. // Place a space before the new string if necessary
  393. if (before.length && !/\s$/.test(before)) {
  394. before += ' ';
  395. }
  396. // Place a space after the new string if necessary
  397. // Will also place a space if at the end of the text
  398. if (!/^\s/.test(after)) {
  399. after = ' ' + after;
  400. }
  401. return before + stringToInsert + after;
  402. }
  403. /**
  404. * Check if a clicked link will open a new window or tab.
  405. *
  406. * We don't have to delay link clicks if they will open a new tab via
  407. * command / shift / control key modifiers or if the 'target' attribute is '_blank'.
  408. *
  409. * @param {$.Event} evt The event object from the original click event.
  410. * @param {jQuery} $link jQuery wrapped link object.
  411. * @returns {boolean}
  412. */
  413. var willOpenNewWindow = function (evt, $link) {
  414. if (!$link) {
  415. $link = $(evt.currentTarget);
  416. }
  417. return $link.attr('target') === '_blank' ||
  418. evt.ctrlKey || // For Windows and Linux
  419. evt.metaKey || // For OS X
  420. evt.shiftKey || // For new window
  421. evt.altKey; // God knows what
  422. };
  423. var LINK_CLICK_RECENCY_TIMEOUT = 500; /* ms */
  424. /**
  425. * Determine if a click event should be logged to our analytics servers.
  426. *
  427. * We currently log clicks only if the link's href is intended to cause
  428. * the parent page to change locations (disregarding modifier keys, etc.).
  429. *
  430. * @param {$.Event} evt The jQuery event object.
  431. * @param {jQuery} $link jQuery wrapped link object.
  432. * @returns {boolean}
  433. */
  434. var clickShouldBeLogged = (function () {
  435. var logged = {};
  436. var urlFragmentReg = /#.*$/;
  437. var getLinkTrackingId = function ($link) {
  438. // Because a link's href may be modified during the course
  439. // of a click event, we need to attach our own IDs.
  440. var id = $link.attr('data-tid');
  441. if (!id) {
  442. id = _.uniqueId();
  443. $link.attr('data-tid', id);
  444. }
  445. return id;
  446. };
  447. return function (evt, $link) {
  448. // We don't do anything if we're already handling this event elsewhere.
  449. if (evt.isDefaultPrevented()) {
  450. return false;
  451. }
  452. // We don't bother if the clicked element is not a link.
  453. if (!$link.is('a')) {
  454. return false;
  455. }
  456. // We don't bother if the clicked link doesn't have an href attribute,
  457. // or if the clicked link's href attribute is just a hash.
  458. var href = ($link.attr('href') || '').replace(urlFragmentReg, '');
  459. if (!href) {
  460. return false;
  461. }
  462. // Because we re-trigger clicks, it is very possible that we're being
  463. // asked to log the same click twice. We keep an internal hash of
  464. // links clicks we've logged within the last .5s so that we don't
  465. // enter an infinite click loop.
  466. var linkId = getLinkTrackingId($link);
  467. var timestamp = new Date().getTime();
  468. // If we recently delayed this click, then bail out now.
  469. if (logged[linkId] && timestamp - logged[linkId] < LINK_CLICK_RECENCY_TIMEOUT) {
  470. return false;
  471. }
  472. logged[linkId] = timestamp;
  473. return true;
  474. };
  475. })();
  476. /**
  477. * This is a workaround for creating a deep-clone, since underscore.clone
  478. * and Object.assign only create shallow-copies
  479. * NOTE: This function is incompatible with values that don't have an
  480. * equivalent type in JSON (ie: Date, Function, Infinity). Additionally
  481. * undefined properties will be omitted from the cloned object.
  482. *
  483. * Ref: https://github.com/jashkenas/underscore/pull/595#issuecomment-34858191
  484. *
  485. * @param {*} original - The item that will be cloned
  486. * @returns {*}
  487. */
  488. function deepClone(original) {
  489. if (typeof original === 'undefined') {
  490. return null;
  491. }
  492. return JSON.parse(JSON.stringify(original));
  493. }
  494. return {
  495. validateEmail: validateEmail,
  496. isUrl: isUrl,
  497. bleachFindUrls: bleachFindUrls,
  498. niceTruncate: niceTruncate,
  499. transitionEndEvent: transitionEndEvent,
  500. animationEndEvent: animationEndEvent,
  501. isMobileUserAgent: isMobileUserAgent,
  502. preventDefaultHandler: preventDefaultHandler,
  503. stopPropagationHandler: stopPropagationHandler,
  504. stopEventHandler: stopEventHandler,
  505. getDomain: getDomain,
  506. serializeArgs: serializeArgs,
  507. serialize: serialize,
  508. openWindow: openWindow,
  509. insertWithWhitespace: insertWithWhitespace,
  510. willOpenNewWindow: willOpenNewWindow,
  511. clickShouldBeLogged: clickShouldBeLogged,
  512. deepClone: deepClone,
  513. browser: browser,
  514. };
  515. });
  516. // https://c.disquscdn.com/next/next-core/core/utils.js

comments powered by Disqus