util.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. /**
  2. * @module zrender/core/util
  3. */
  4. // 用于处理merge时无法遍历Date等对象的问题
  5. var BUILTIN_OBJECT = {
  6. '[object Function]': 1,
  7. '[object RegExp]': 1,
  8. '[object Date]': 1,
  9. '[object Error]': 1,
  10. '[object CanvasGradient]': 1,
  11. '[object CanvasPattern]': 1,
  12. // For node-canvas
  13. '[object Image]': 1,
  14. '[object Canvas]': 1
  15. };
  16. var TYPED_ARRAY = {
  17. '[object Int8Array]': 1,
  18. '[object Uint8Array]': 1,
  19. '[object Uint8ClampedArray]': 1,
  20. '[object Int16Array]': 1,
  21. '[object Uint16Array]': 1,
  22. '[object Int32Array]': 1,
  23. '[object Uint32Array]': 1,
  24. '[object Float32Array]': 1,
  25. '[object Float64Array]': 1
  26. };
  27. var objToString = Object.prototype.toString;
  28. var arrayProto = Array.prototype;
  29. var nativeForEach = arrayProto.forEach;
  30. var nativeFilter = arrayProto.filter;
  31. var nativeSlice = arrayProto.slice;
  32. var nativeMap = arrayProto.map;
  33. var nativeReduce = arrayProto.reduce; // Avoid assign to an exported variable, for transforming to cjs.
  34. var methods = {};
  35. function $override(name, fn) {
  36. // Clear ctx instance for different environment
  37. if (name === 'createCanvas') {
  38. _ctx = null;
  39. }
  40. methods[name] = fn;
  41. }
  42. /**
  43. * Those data types can be cloned:
  44. * Plain object, Array, TypedArray, number, string, null, undefined.
  45. * Those data types will be assgined using the orginal data:
  46. * BUILTIN_OBJECT
  47. * Instance of user defined class will be cloned to a plain object, without
  48. * properties in prototype.
  49. * Other data types is not supported (not sure what will happen).
  50. *
  51. * Caution: do not support clone Date, for performance consideration.
  52. * (There might be a large number of date in `series.data`).
  53. * So date should not be modified in and out of echarts.
  54. *
  55. * @param {*} source
  56. * @return {*} new
  57. */
  58. function clone(source) {
  59. if (source == null || typeof source !== 'object') {
  60. return source;
  61. }
  62. var result = source;
  63. var typeStr = objToString.call(source);
  64. if (typeStr === '[object Array]') {
  65. if (!isPrimitive(source)) {
  66. result = [];
  67. for (var i = 0, len = source.length; i < len; i++) {
  68. result[i] = clone(source[i]);
  69. }
  70. }
  71. } else if (TYPED_ARRAY[typeStr]) {
  72. if (!isPrimitive(source)) {
  73. var Ctor = source.constructor;
  74. if (source.constructor.from) {
  75. result = Ctor.from(source);
  76. } else {
  77. result = new Ctor(source.length);
  78. for (var i = 0, len = source.length; i < len; i++) {
  79. result[i] = clone(source[i]);
  80. }
  81. }
  82. }
  83. } else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
  84. result = {};
  85. for (var key in source) {
  86. if (source.hasOwnProperty(key)) {
  87. result[key] = clone(source[key]);
  88. }
  89. }
  90. }
  91. return result;
  92. }
  93. /**
  94. * @memberOf module:zrender/core/util
  95. * @param {*} target
  96. * @param {*} source
  97. * @param {boolean} [overwrite=false]
  98. */
  99. function merge(target, source, overwrite) {
  100. // We should escapse that source is string
  101. // and enter for ... in ...
  102. if (!isObject(source) || !isObject(target)) {
  103. return overwrite ? clone(source) : target;
  104. }
  105. for (var key in source) {
  106. if (source.hasOwnProperty(key)) {
  107. var targetProp = target[key];
  108. var sourceProp = source[key];
  109. if (isObject(sourceProp) && isObject(targetProp) && !isArray(sourceProp) && !isArray(targetProp) && !isDom(sourceProp) && !isDom(targetProp) && !isBuiltInObject(sourceProp) && !isBuiltInObject(targetProp) && !isPrimitive(sourceProp) && !isPrimitive(targetProp)) {
  110. // 如果需要递归覆盖,就递归调用merge
  111. merge(targetProp, sourceProp, overwrite);
  112. } else if (overwrite || !(key in target)) {
  113. // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
  114. // NOTE,在 target[key] 不存在的时候也是直接覆盖
  115. target[key] = clone(source[key], true);
  116. }
  117. }
  118. }
  119. return target;
  120. }
  121. /**
  122. * @param {Array} targetAndSources The first item is target, and the rests are source.
  123. * @param {boolean} [overwrite=false]
  124. * @return {*} target
  125. */
  126. function mergeAll(targetAndSources, overwrite) {
  127. var result = targetAndSources[0];
  128. for (var i = 1, len = targetAndSources.length; i < len; i++) {
  129. result = merge(result, targetAndSources[i], overwrite);
  130. }
  131. return result;
  132. }
  133. /**
  134. * @param {*} target
  135. * @param {*} source
  136. * @memberOf module:zrender/core/util
  137. */
  138. function extend(target, source) {
  139. for (var key in source) {
  140. if (source.hasOwnProperty(key)) {
  141. target[key] = source[key];
  142. }
  143. }
  144. return target;
  145. }
  146. /**
  147. * @param {*} target
  148. * @param {*} source
  149. * @param {boolean} [overlay=false]
  150. * @memberOf module:zrender/core/util
  151. */
  152. function defaults(target, source, overlay) {
  153. for (var key in source) {
  154. if (source.hasOwnProperty(key) && (overlay ? source[key] != null : target[key] == null)) {
  155. target[key] = source[key];
  156. }
  157. }
  158. return target;
  159. }
  160. var createCanvas = function () {
  161. return methods.createCanvas();
  162. };
  163. methods.createCanvas = function () {
  164. return document.createElement('canvas');
  165. }; // FIXME
  166. var _ctx;
  167. function getContext() {
  168. if (!_ctx) {
  169. // Use util.createCanvas instead of createCanvas
  170. // because createCanvas may be overwritten in different environment
  171. _ctx = createCanvas().getContext('2d');
  172. }
  173. return _ctx;
  174. }
  175. /**
  176. * 查询数组中元素的index
  177. * @memberOf module:zrender/core/util
  178. */
  179. function indexOf(array, value) {
  180. if (array) {
  181. if (array.indexOf) {
  182. return array.indexOf(value);
  183. }
  184. for (var i = 0, len = array.length; i < len; i++) {
  185. if (array[i] === value) {
  186. return i;
  187. }
  188. }
  189. }
  190. return -1;
  191. }
  192. /**
  193. * 构造类继承关系
  194. *
  195. * @memberOf module:zrender/core/util
  196. * @param {Function} clazz 源类
  197. * @param {Function} baseClazz 基类
  198. */
  199. function inherits(clazz, baseClazz) {
  200. var clazzPrototype = clazz.prototype;
  201. function F() {}
  202. F.prototype = baseClazz.prototype;
  203. clazz.prototype = new F();
  204. for (var prop in clazzPrototype) {
  205. if (clazzPrototype.hasOwnProperty(prop)) {
  206. clazz.prototype[prop] = clazzPrototype[prop];
  207. }
  208. }
  209. clazz.prototype.constructor = clazz;
  210. clazz.superClass = baseClazz;
  211. }
  212. /**
  213. * @memberOf module:zrender/core/util
  214. * @param {Object|Function} target
  215. * @param {Object|Function} sorce
  216. * @param {boolean} overlay
  217. */
  218. function mixin(target, source, overlay) {
  219. target = 'prototype' in target ? target.prototype : target;
  220. source = 'prototype' in source ? source.prototype : source;
  221. defaults(target, source, overlay);
  222. }
  223. /**
  224. * Consider typed array.
  225. * @param {Array|TypedArray} data
  226. */
  227. function isArrayLike(data) {
  228. if (!data) {
  229. return;
  230. }
  231. if (typeof data === 'string') {
  232. return false;
  233. }
  234. return typeof data.length === 'number';
  235. }
  236. /**
  237. * 数组或对象遍历
  238. * @memberOf module:zrender/core/util
  239. * @param {Object|Array} obj
  240. * @param {Function} cb
  241. * @param {*} [context]
  242. */
  243. function each(obj, cb, context) {
  244. if (!(obj && cb)) {
  245. return;
  246. }
  247. if (obj.forEach && obj.forEach === nativeForEach) {
  248. obj.forEach(cb, context);
  249. } else if (obj.length === +obj.length) {
  250. for (var i = 0, len = obj.length; i < len; i++) {
  251. cb.call(context, obj[i], i, obj);
  252. }
  253. } else {
  254. for (var key in obj) {
  255. if (obj.hasOwnProperty(key)) {
  256. cb.call(context, obj[key], key, obj);
  257. }
  258. }
  259. }
  260. }
  261. /**
  262. * 数组映射
  263. * @memberOf module:zrender/core/util
  264. * @param {Array} obj
  265. * @param {Function} cb
  266. * @param {*} [context]
  267. * @return {Array}
  268. */
  269. function map(obj, cb, context) {
  270. if (!(obj && cb)) {
  271. return;
  272. }
  273. if (obj.map && obj.map === nativeMap) {
  274. return obj.map(cb, context);
  275. } else {
  276. var result = [];
  277. for (var i = 0, len = obj.length; i < len; i++) {
  278. result.push(cb.call(context, obj[i], i, obj));
  279. }
  280. return result;
  281. }
  282. }
  283. /**
  284. * @memberOf module:zrender/core/util
  285. * @param {Array} obj
  286. * @param {Function} cb
  287. * @param {Object} [memo]
  288. * @param {*} [context]
  289. * @return {Array}
  290. */
  291. function reduce(obj, cb, memo, context) {
  292. if (!(obj && cb)) {
  293. return;
  294. }
  295. if (obj.reduce && obj.reduce === nativeReduce) {
  296. return obj.reduce(cb, memo, context);
  297. } else {
  298. for (var i = 0, len = obj.length; i < len; i++) {
  299. memo = cb.call(context, memo, obj[i], i, obj);
  300. }
  301. return memo;
  302. }
  303. }
  304. /**
  305. * 数组过滤
  306. * @memberOf module:zrender/core/util
  307. * @param {Array} obj
  308. * @param {Function} cb
  309. * @param {*} [context]
  310. * @return {Array}
  311. */
  312. function filter(obj, cb, context) {
  313. if (!(obj && cb)) {
  314. return;
  315. }
  316. if (obj.filter && obj.filter === nativeFilter) {
  317. return obj.filter(cb, context);
  318. } else {
  319. var result = [];
  320. for (var i = 0, len = obj.length; i < len; i++) {
  321. if (cb.call(context, obj[i], i, obj)) {
  322. result.push(obj[i]);
  323. }
  324. }
  325. return result;
  326. }
  327. }
  328. /**
  329. * 数组项查找
  330. * @memberOf module:zrender/core/util
  331. * @param {Array} obj
  332. * @param {Function} cb
  333. * @param {*} [context]
  334. * @return {*}
  335. */
  336. function find(obj, cb, context) {
  337. if (!(obj && cb)) {
  338. return;
  339. }
  340. for (var i = 0, len = obj.length; i < len; i++) {
  341. if (cb.call(context, obj[i], i, obj)) {
  342. return obj[i];
  343. }
  344. }
  345. }
  346. /**
  347. * @memberOf module:zrender/core/util
  348. * @param {Function} func
  349. * @param {*} context
  350. * @return {Function}
  351. */
  352. function bind(func, context) {
  353. var args = nativeSlice.call(arguments, 2);
  354. return function () {
  355. return func.apply(context, args.concat(nativeSlice.call(arguments)));
  356. };
  357. }
  358. /**
  359. * @memberOf module:zrender/core/util
  360. * @param {Function} func
  361. * @return {Function}
  362. */
  363. function curry(func) {
  364. var args = nativeSlice.call(arguments, 1);
  365. return function () {
  366. return func.apply(this, args.concat(nativeSlice.call(arguments)));
  367. };
  368. }
  369. /**
  370. * @memberOf module:zrender/core/util
  371. * @param {*} value
  372. * @return {boolean}
  373. */
  374. function isArray(value) {
  375. return objToString.call(value) === '[object Array]';
  376. }
  377. /**
  378. * @memberOf module:zrender/core/util
  379. * @param {*} value
  380. * @return {boolean}
  381. */
  382. function isFunction(value) {
  383. return typeof value === 'function';
  384. }
  385. /**
  386. * @memberOf module:zrender/core/util
  387. * @param {*} value
  388. * @return {boolean}
  389. */
  390. function isString(value) {
  391. return objToString.call(value) === '[object String]';
  392. }
  393. /**
  394. * @memberOf module:zrender/core/util
  395. * @param {*} value
  396. * @return {boolean}
  397. */
  398. function isObject(value) {
  399. // Avoid a V8 JIT bug in Chrome 19-20.
  400. // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
  401. var type = typeof value;
  402. return type === 'function' || !!value && type === 'object';
  403. }
  404. /**
  405. * @memberOf module:zrender/core/util
  406. * @param {*} value
  407. * @return {boolean}
  408. */
  409. function isBuiltInObject(value) {
  410. return !!BUILTIN_OBJECT[objToString.call(value)];
  411. }
  412. /**
  413. * @memberOf module:zrender/core/util
  414. * @param {*} value
  415. * @return {boolean}
  416. */
  417. function isTypedArray(value) {
  418. return !!TYPED_ARRAY[objToString.call(value)];
  419. }
  420. /**
  421. * @memberOf module:zrender/core/util
  422. * @param {*} value
  423. * @return {boolean}
  424. */
  425. function isDom(value) {
  426. return typeof value === 'object' && typeof value.nodeType === 'number' && typeof value.ownerDocument === 'object';
  427. }
  428. /**
  429. * Whether is exactly NaN. Notice isNaN('a') returns true.
  430. * @param {*} value
  431. * @return {boolean}
  432. */
  433. function eqNaN(value) {
  434. /* eslint-disable-next-line no-self-compare */
  435. return value !== value;
  436. }
  437. /**
  438. * If value1 is not null, then return value1, otherwise judget rest of values.
  439. * Low performance.
  440. * @memberOf module:zrender/core/util
  441. * @return {*} Final value
  442. */
  443. function retrieve(values) {
  444. for (var i = 0, len = arguments.length; i < len; i++) {
  445. if (arguments[i] != null) {
  446. return arguments[i];
  447. }
  448. }
  449. }
  450. function retrieve2(value0, value1) {
  451. return value0 != null ? value0 : value1;
  452. }
  453. function retrieve3(value0, value1, value2) {
  454. return value0 != null ? value0 : value1 != null ? value1 : value2;
  455. }
  456. /**
  457. * @memberOf module:zrender/core/util
  458. * @param {Array} arr
  459. * @param {number} startIndex
  460. * @param {number} endIndex
  461. * @return {Array}
  462. */
  463. function slice() {
  464. return Function.call.apply(nativeSlice, arguments);
  465. }
  466. /**
  467. * Normalize css liked array configuration
  468. * e.g.
  469. * 3 => [3, 3, 3, 3]
  470. * [4, 2] => [4, 2, 4, 2]
  471. * [4, 3, 2] => [4, 3, 2, 3]
  472. * @param {number|Array.<number>} val
  473. * @return {Array.<number>}
  474. */
  475. function normalizeCssArray(val) {
  476. if (typeof val === 'number') {
  477. return [val, val, val, val];
  478. }
  479. var len = val.length;
  480. if (len === 2) {
  481. // vertical | horizontal
  482. return [val[0], val[1], val[0], val[1]];
  483. } else if (len === 3) {
  484. // top | horizontal | bottom
  485. return [val[0], val[1], val[2], val[1]];
  486. }
  487. return val;
  488. }
  489. /**
  490. * @memberOf module:zrender/core/util
  491. * @param {boolean} condition
  492. * @param {string} message
  493. */
  494. function assert(condition, message) {
  495. if (!condition) {
  496. throw new Error(message);
  497. }
  498. }
  499. /**
  500. * @memberOf module:zrender/core/util
  501. * @param {string} str string to be trimed
  502. * @return {string} trimed string
  503. */
  504. function trim(str) {
  505. if (str == null) {
  506. return null;
  507. } else if (typeof str.trim === 'function') {
  508. return str.trim();
  509. } else {
  510. return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
  511. }
  512. }
  513. var primitiveKey = '__ec_primitive__';
  514. /**
  515. * Set an object as primitive to be ignored traversing children in clone or merge
  516. */
  517. function setAsPrimitive(obj) {
  518. obj[primitiveKey] = true;
  519. }
  520. function isPrimitive(obj) {
  521. return obj[primitiveKey];
  522. }
  523. /**
  524. * @constructor
  525. * @param {Object} obj Only apply `ownProperty`.
  526. */
  527. function HashMap(obj) {
  528. var isArr = isArray(obj); // Key should not be set on this, otherwise
  529. // methods get/set/... may be overrided.
  530. this.data = {};
  531. var thisMap = this;
  532. obj instanceof HashMap ? obj.each(visit) : obj && each(obj, visit);
  533. function visit(value, key) {
  534. isArr ? thisMap.set(value, key) : thisMap.set(key, value);
  535. }
  536. }
  537. HashMap.prototype = {
  538. constructor: HashMap,
  539. // Do not provide `has` method to avoid defining what is `has`.
  540. // (We usually treat `null` and `undefined` as the same, different
  541. // from ES6 Map).
  542. get: function (key) {
  543. return this.data.hasOwnProperty(key) ? this.data[key] : null;
  544. },
  545. set: function (key, value) {
  546. // Comparing with invocation chaining, `return value` is more commonly
  547. // used in this case: `var someVal = map.set('a', genVal());`
  548. return this.data[key] = value;
  549. },
  550. // Although util.each can be performed on this hashMap directly, user
  551. // should not use the exposed keys, who are prefixed.
  552. each: function (cb, context) {
  553. context !== void 0 && (cb = bind(cb, context));
  554. /* eslint-disable guard-for-in */
  555. for (var key in this.data) {
  556. this.data.hasOwnProperty(key) && cb(this.data[key], key);
  557. }
  558. /* eslint-enable guard-for-in */
  559. },
  560. // Do not use this method if performance sensitive.
  561. removeKey: function (key) {
  562. delete this.data[key];
  563. }
  564. };
  565. function createHashMap(obj) {
  566. return new HashMap(obj);
  567. }
  568. function concatArray(a, b) {
  569. var newArray = new a.constructor(a.length + b.length);
  570. for (var i = 0; i < a.length; i++) {
  571. newArray[i] = a[i];
  572. }
  573. var offset = a.length;
  574. for (i = 0; i < b.length; i++) {
  575. newArray[i + offset] = b[i];
  576. }
  577. return newArray;
  578. }
  579. function noop() {}
  580. exports.$override = $override;
  581. exports.clone = clone;
  582. exports.merge = merge;
  583. exports.mergeAll = mergeAll;
  584. exports.extend = extend;
  585. exports.defaults = defaults;
  586. exports.createCanvas = createCanvas;
  587. exports.getContext = getContext;
  588. exports.indexOf = indexOf;
  589. exports.inherits = inherits;
  590. exports.mixin = mixin;
  591. exports.isArrayLike = isArrayLike;
  592. exports.each = each;
  593. exports.map = map;
  594. exports.reduce = reduce;
  595. exports.filter = filter;
  596. exports.find = find;
  597. exports.bind = bind;
  598. exports.curry = curry;
  599. exports.isArray = isArray;
  600. exports.isFunction = isFunction;
  601. exports.isString = isString;
  602. exports.isObject = isObject;
  603. exports.isBuiltInObject = isBuiltInObject;
  604. exports.isTypedArray = isTypedArray;
  605. exports.isDom = isDom;
  606. exports.eqNaN = eqNaN;
  607. exports.retrieve = retrieve;
  608. exports.retrieve2 = retrieve2;
  609. exports.retrieve3 = retrieve3;
  610. exports.slice = slice;
  611. exports.normalizeCssArray = normalizeCssArray;
  612. exports.assert = assert;
  613. exports.trim = trim;
  614. exports.setAsPrimitive = setAsPrimitive;
  615. exports.isPrimitive = isPrimitive;
  616. exports.createHashMap = createHashMap;
  617. exports.concatArray = concatArray;
  618. exports.noop = noop;