utils.js 7.7 KB

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