utils.js 7.8 KB

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