util.js 15 KB

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