utils.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. var config = require('./config'),
  2. toString = ({}).toString,
  3. win = window,
  4. console = win.console,
  5. def = Object.defineProperty,
  6. OBJECT = 'object',
  7. THIS_RE = /[^\w]this[^\w]/,
  8. BRACKET_RE_S = /\['([^']+)'\]/g,
  9. BRACKET_RE_D = /\["([^"]+)"\]/g,
  10. ViewModel // late def
  11. var defer =
  12. win.requestAnimationFrame ||
  13. win.webkitRequestAnimationFrame ||
  14. win.setTimeout
  15. /**
  16. * Normalize keypath with possible brackets into dot notations
  17. */
  18. function normalizeKeypath (key) {
  19. return key.indexOf('[') < 0
  20. ? key
  21. : key.replace(BRACKET_RE_S, '.$1')
  22. .replace(BRACKET_RE_D, '.$1')
  23. }
  24. var utils = module.exports = {
  25. /**
  26. * Convert a string template to a dom fragment
  27. */
  28. toFragment: require('./fragment'),
  29. /**
  30. * Parse the various types of template options
  31. */
  32. parseTemplateOption: require('./template-parser.js'),
  33. /**
  34. * get a value from an object keypath
  35. */
  36. get: function (obj, key) {
  37. /* jshint eqeqeq: false */
  38. key = normalizeKeypath(key)
  39. if (key.indexOf('.') < 0) {
  40. return obj[key]
  41. }
  42. var path = key.split('.'),
  43. d = -1, l = path.length
  44. while (++d < l && obj != null) {
  45. obj = obj[path[d]]
  46. }
  47. return obj
  48. },
  49. /**
  50. * set a value to an object keypath
  51. */
  52. set: function (obj, key, val) {
  53. /* jshint eqeqeq: false */
  54. key = normalizeKeypath(key)
  55. if (key.indexOf('.') < 0) {
  56. obj[key] = val
  57. return
  58. }
  59. var path = key.split('.'),
  60. d = -1, l = path.length - 1
  61. while (++d < l) {
  62. if (obj[path[d]] == null) {
  63. obj[path[d]] = {}
  64. }
  65. obj = obj[path[d]]
  66. }
  67. obj[path[d]] = val
  68. },
  69. /**
  70. * return the base segment of a keypath
  71. */
  72. baseKey: function (key) {
  73. return key.indexOf('.') > 0
  74. ? key.split('.')[0]
  75. : key
  76. },
  77. /**
  78. * Create a prototype-less object
  79. * which is a better hash/map
  80. */
  81. hash: function () {
  82. return Object.create(null)
  83. },
  84. /**
  85. * get an attribute and remove it.
  86. */
  87. attr: function (el, type) {
  88. var attr = config.prefix + '-' + type,
  89. val = el.getAttribute(attr)
  90. if (val !== null) {
  91. el.removeAttribute(attr)
  92. }
  93. return val
  94. },
  95. /**
  96. * Define an ienumerable property
  97. * This avoids it being included in JSON.stringify
  98. * or for...in loops.
  99. */
  100. defProtected: function (obj, key, val, enumerable, writable) {
  101. def(obj, key, {
  102. value : val,
  103. enumerable : enumerable,
  104. writable : writable,
  105. configurable : true
  106. })
  107. },
  108. /**
  109. * A less bullet-proof but more efficient type check
  110. * than Object.prototype.toString
  111. */
  112. isObject: function (obj) {
  113. return typeof obj === OBJECT && obj && !Array.isArray(obj)
  114. },
  115. /**
  116. * A more accurate but less efficient type check
  117. */
  118. isTrueObject: function (obj) {
  119. return toString.call(obj) === '[object Object]'
  120. },
  121. /**
  122. * Most simple bind
  123. * enough for the usecase and fast than native bind()
  124. */
  125. bind: function (fn, ctx) {
  126. return function (arg) {
  127. return fn.call(ctx, arg)
  128. }
  129. },
  130. /**
  131. * Make sure null and undefined output empty string
  132. */
  133. guard: function (value) {
  134. /* jshint eqeqeq: false, eqnull: true */
  135. return value == null
  136. ? ''
  137. : (typeof value == 'object')
  138. ? JSON.stringify(value)
  139. : value
  140. },
  141. /**
  142. * When setting value on the VM, parse possible numbers
  143. */
  144. checkNumber: function (value) {
  145. return (isNaN(value) || value === null || typeof value === 'boolean')
  146. ? value
  147. : Number(value)
  148. },
  149. /**
  150. * simple extend
  151. */
  152. extend: function (obj, ext) {
  153. for (var key in ext) {
  154. if (obj[key] !== ext[key]) {
  155. obj[key] = ext[key]
  156. }
  157. }
  158. return obj
  159. },
  160. /**
  161. * filter an array with duplicates into uniques
  162. */
  163. unique: function (arr) {
  164. var hash = utils.hash(),
  165. i = arr.length,
  166. key, res = []
  167. while (i--) {
  168. key = arr[i]
  169. if (hash[key]) continue
  170. hash[key] = 1
  171. res.push(key)
  172. }
  173. return res
  174. },
  175. /**
  176. * Convert the object to a ViewModel constructor
  177. * if it is not already one
  178. */
  179. toConstructor: function (obj) {
  180. ViewModel = ViewModel || require('./viewmodel')
  181. return utils.isObject(obj)
  182. ? ViewModel.extend(obj)
  183. : typeof obj === 'function'
  184. ? obj
  185. : null
  186. },
  187. /**
  188. * Check if a filter function contains references to `this`
  189. * If yes, mark it as a computed filter.
  190. */
  191. checkFilter: function (filter) {
  192. if (THIS_RE.test(filter.toString())) {
  193. filter.computed = true
  194. }
  195. },
  196. /**
  197. * convert certain option values to the desired format.
  198. */
  199. processOptions: function (options) {
  200. var components = options.components,
  201. partials = options.partials,
  202. template = options.template,
  203. filters = options.filters,
  204. key
  205. if (components) {
  206. for (key in components) {
  207. components[key] = utils.toConstructor(components[key])
  208. }
  209. }
  210. if (partials) {
  211. for (key in partials) {
  212. partials[key] = utils.parseTemplateOption(partials[key])
  213. }
  214. }
  215. if (filters) {
  216. for (key in filters) {
  217. utils.checkFilter(filters[key])
  218. }
  219. }
  220. if (template) {
  221. options.template = utils.parseTemplateOption(template)
  222. }
  223. },
  224. /**
  225. * used to defer batch updates
  226. */
  227. nextTick: function (cb, context) {
  228. if (context) {
  229. defer(utils.bind(cb, context), 0)
  230. }
  231. else {
  232. defer(cb, 0)
  233. }
  234. },
  235. /**
  236. * add class for IE
  237. * uses classList if available
  238. */
  239. addClass: function (el, cls) {
  240. if (el.classList) {
  241. el.classList.add(cls)
  242. } else {
  243. var cur = ' ' + utils.getClassName(el) + ' '
  244. if (cur.indexOf(' ' + cls + ' ') < 0) {
  245. el.setAttribute('class', (cur + cls).trim())
  246. }
  247. }
  248. },
  249. /**
  250. * remove class for IE
  251. */
  252. removeClass: function (el, cls) {
  253. if (el.classList) {
  254. el.classList.remove(cls)
  255. } else {
  256. var cur = ' ' + utils.getClassName(el) + ' ',
  257. tar = ' ' + cls + ' '
  258. while (cur.indexOf(tar) >= 0) {
  259. cur = cur.replace(tar, ' ')
  260. }
  261. el.setAttribute('class', cur.trim())
  262. }
  263. },
  264. /**
  265. * get class name for IE
  266. */
  267. getClassName: function(el) {
  268. return (el.className instanceof SVGAnimatedString ? el.className.baseVal : el.className)
  269. },
  270. /**
  271. * Convert an object to Array
  272. * used in v-repeat and array filters
  273. */
  274. objectToArray: function (obj) {
  275. var res = [], val, data
  276. for (var key in obj) {
  277. val = obj[key]
  278. data = utils.isObject(val)
  279. ? val
  280. : { $value: val }
  281. data.$key = key
  282. res.push(data)
  283. }
  284. return res
  285. }
  286. }
  287. enableDebug()
  288. function enableDebug () {
  289. /**
  290. * log for debugging
  291. */
  292. utils.log = function (msg) {
  293. if (config.debug && console) {
  294. console.log(msg)
  295. }
  296. }
  297. /**
  298. * warnings, traces by default
  299. * can be suppressed by `silent` option.
  300. */
  301. utils.warn = function (msg) {
  302. if (!config.silent && console) {
  303. console.warn(msg)
  304. if (config.debug && console.trace) {
  305. console.trace()
  306. }
  307. }
  308. }
  309. }