├── .gitignore ├── .npmignore ├── README.md ├── build └── index.coffee ├── karma.conf.coffee ├── package.json ├── src ├── _throttledListener.coffee ├── class.coffee ├── dynamicCss.coffee ├── fragToString.coffee ├── getDocumentHeight.coffee ├── getViewportOffset.coffee ├── getViewportSize.coffee ├── getVue.coffee ├── isOpened.coffee ├── isOpened2.coffee ├── onClick.coffee ├── onClickStack.coffee ├── onClickStore.coffee ├── onDocument.coffee ├── onElementResize.coffee ├── onMouseMove.coffee ├── onResize.coffee ├── onWindowResize.coffee ├── onWindowScroll.coffee ├── onceDocument.coffee ├── parentListener.coffee ├── parentListener2.coffee ├── setCss.coffee ├── style.coffee ├── transition.coffee ├── transition2.coffee └── vue.coffee ├── test ├── class.coffee ├── dynamicCss.coffee ├── fragToString.coffee ├── getViewportOffset.coffee ├── getViewportSize.coffee ├── getVue.coffee ├── onClick.coffee ├── onClickStack.coffee ├── onClickStore.coffee ├── onDocument.coffee ├── onElementResize.coffee ├── onMouseMove.coffee ├── onWindowResize.coffee ├── onceDocument.coffee ├── setCss.coffee ├── style.coffee └── vue.coffee └── webpack.config.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sublime-project 3 | *.sublime-workspace 4 | npm-debug.log 5 | build/*.js 6 | *.js 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.sublime-project 3 | *.sublime-workspace 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-mixins 2 | 3 | A collection of mixins in vue. 4 | Heavily used in [**vue-comps**](https://github.com/vue-comps). 5 | 6 | ## Policy 7 | 8 | all sorts of mixins can be submitted. There will be no removes because of deprecation. If the API of a mixin changes the name has to change, for example `onResize` -> `onResize2` 9 | 10 | ## Install 11 | 12 | ```sh 13 | npm install --save-dev vue-mixins 14 | ``` 15 | or include `bundle.js` 16 | ## Usage 17 | ```coffee 18 | ## whithin your module 19 | components: 20 | mixins:[ 21 | require("vue-mixins/onClick") 22 | ] 23 | # if you have used the bundle.js 24 | components: 25 | mixins:[ 26 | window.vueMixins.onClick 27 | ] 28 | ``` 29 | ## List of mixins 30 | Name | src| description 31 | ---: | ---| ------- 32 | [getViewportSize](https://github.com/paulpflug/vue-mixins#getviewportsize) | [src](src/getViewportSize.coffee) | adds a method `getViewportSize` which returns an object containing the `width` and `height` of the viewport 33 | [getDocumentHeight](https://github.com/paulpflug/vue-mixins#getdocumentheight) | [src](src/getDocumentHeight.coffee) | adds a method `getDocumentHeight` which returns the `height` of the document 34 | [onceDocument](https://github.com/paulpflug/vue-mixins#oncedocument) | [src](src/onceDocument.coffee) | adds a eventListener to the document which removes itself after first successful call| 35 | |[onClick](https://github.com/paulpflug/vue-mixins#onclick) | [src](src/onClick.coffee) | adds a method `click` which will call the function `onClick` if set 36 | [onClickStack](https://github.com/paulpflug/vue-mixins#onclickstack) | [src](src/onClickStack.coffee) | adds two methods: `click` and `addToClickStack` 37 | [onClickStore](https://github.com/paulpflug/vue-mixins#onclickstore) | [src](src/onClickStore.coffee) | adds two methods: `click` and `onClick` (see below) 38 | [onDocument](https://github.com/paulpflug/vue-mixins#ondocument) | [src](src/onDocument.coffee) | like `onceDocument` but doesn't removes itself 39 | onResize | [src](src/onResize.coffee) | deprecated 40 | [onWindowResize](https://github.com/paulpflug/vue-mixins#onwindowresize) | [src](src/onWindowResize.coffee) | fires on resize of window (throttled and bundled) 41 | [onElementResize](https://github.com/paulpflug/vue-mixins#onelementresize) | [src](src/onElementResize.coffee) | fires on resize of window or element, but only if the dimensions of the element changed 42 | [onWindowScroll](https://github.com/paulpflug/vue-mixins#onwindowscroll) | [src](src/onWindowScroll.coffee) | fires on scroll on window (throttled and bundled) 43 | [setCss](https://github.com/paulpflug/vue-mixins#setcss) | [src](src/setCss.coffee) | set Css of another element 44 | [dynamicCss](https://github.com/paulpflug/vue-mixins#dynamiccss) | [src](src/dynamicCss.coffee) | dynamically manipulate css stylesheet 45 | getVue | [src](src/getVue.coffee) | deprecated, use `vue` instead 46 | [vue](https://github.com/paulpflug/vue-mixins#vue) | [src](src/vue.coffee) | adds a computed property `Vue` with the current instance of `Vue` 47 | [isOpened](https://github.com/paulpflug/vue-mixins#isopened) | [src](src/isOpened.coffee) | adds everything for opened state management (two-way) 48 | [isOpened2](https://github.com/paulpflug/vue-mixins#isopened) | [src](src/isOpened2.coffee) | same as isOpened but for vue 2.0 (one-way) 49 | [parentListener](https://github.com/paulpflug/vue-mixins#parentlistener) | [src](src/parentListener.coffee) | hooks a function upon parent click 50 | [parentListener2](https://github.com/paulpflug/vue-mixins#parentlistener) | [src](src/parentListener2.coffee) | same as parentListener but for vue 2.0 51 | [fragToString](https://github.com/paulpflug/vue-mixins#fragtostring) | [src](src/fragToString.coffee) | converts a `documentFragment` to `String` 52 | [class](https://github.com/paulpflug/vue-mixins#class) | [src](src/class.coffee) | used to create a properly merged vue class object/array from a given prop and another vue class object/array 53 | [style](https://github.com/paulpflug/vue-mixins#style) | [src](src/style.coffee) | used to create a properly merged vue style object/array from a given prop and another vue style object/array 54 | [transition](https://github.com/paulpflug/vue-mixins#transition) | [src](src/transition.coffee) | used to manage user provided transition in a reusable component 55 | [transition2](https://github.com/paulpflug/vue-mixins#transition) | [src](src/transition2.coffee) | same as transition but for vue 2.0 56 | [onMouseMove](https://github.com/paulpflug/vue-mixins#onmousemove) | [src](src/onMouseMove.coffee) | fires on move of the mouse (throttled and bundled) 57 | 58 | ## Detailed usage 59 | ### getViewportSize 60 | ```js 61 | // adds a method: 62 | // getViewportSize() 63 | // 64 | // usage: 65 | vs = this.getViewportSize() 66 | vs.width 67 | vs.height 68 | ``` 69 | ### getDocumentHeight 70 | ```js 71 | // adds a method: 72 | // getDocumentHeight() 73 | // 74 | // usage: 75 | height = this.getDocumentHeight() 76 | ``` 77 | ### onceDocument 78 | ```js 79 | // adds a method: 80 | // onceDocument(event, cb, useCapture) 81 | // 82 | // usage: 83 | dispose = this.onceDocument('click',function(e){ 84 | doSomething() 85 | // return true will remove the listener 86 | // return false will not remove the listener 87 | },false) 88 | dispose() // will remove the listener 89 | ``` 90 | ### onClick 91 | ```js 92 | // adds a method: 93 | // click(event) which will call this.onClick(e) if available 94 | // 95 | // usage: 96 | this.onClick = function(e) {doSomething()} 97 | ``` 98 | ```html 99 | 100 |
101 | ``` 102 | 103 | ### onClickStack 104 | ```js 105 | // adds two methods: 106 | // - click(event) will call the last function in this.onClickStack if available 107 | // - addToClickStack(fn) will add a function to this.onClickStack and return a function to dispose it 108 | // 109 | // usage: 110 | var dispose = null 111 | var cb = function(e) { 112 | doSomething() 113 | dispose() // to remove from stack 114 | } 115 | dispose = this.addToClickStack(cb) 116 | ``` 117 | ```html 118 | 119 |
120 | ``` 121 | 122 | ### onClickStore 123 | ```js 124 | // adds two methods: 125 | // - click(event) will call all functions in this.onClickStore 126 | // - onClick(fn) will add a function to this.onClickStore and return a function to dispose it 127 | // 128 | // usage: 129 | var dispose = null 130 | var cb = function(e) { 131 | doSomething() 132 | dispose() // to remove from store 133 | } 134 | dispose = this.onClickStore(cb) 135 | ``` 136 | ```html 137 | 138 |
139 | ``` 140 | ### onDocument 141 | like `onceDocument`, but doesn't remove itself on first successful invokation. 142 | 143 | ### onWindowResize 144 | ```js 145 | // adds a method: onWindowResize(cb) which will return a function to dispose it 146 | // 147 | // usage: 148 | dispose = this.onWindowResize(function(){/*doSomethingOnWindowResize*/}) 149 | // remove your cb 150 | dispose() 151 | // all events will be automatically disposed on `beforeDestroy` 152 | ``` 153 | 154 | ### onElementResize 155 | ```js 156 | // adds a method: onElementResize(el, cb) which will return a function to dispose it 157 | // 158 | // usage: 159 | dispose = this.onElementResize(el, function(){/*doSomethingOnElementResize*/}) 160 | // remove your cb 161 | dispose() 162 | // all events will be automatically disposed on `beforeDestroy` 163 | ``` 164 | 165 | ### onWindowScroll 166 | ```js 167 | // adds a method: onWindowScroll(cb) which will return a function to dispose it 168 | // 169 | // usage: 170 | dispose = this.onWindowScroll(function(){/*doSomethingOnWindowScroll*/}) 171 | // remove your cb 172 | dispose() 173 | // all events will be automatically disposed on `beforeDestroy` 174 | ``` 175 | 176 | ### setCss 177 | ```js 178 | // adds a method: 179 | // setCss(element,cssProperty, cssValue) 180 | // 181 | // usage: 182 | this.setCss(document.body,"overflow","hidden") 183 | 184 | // remove overflow from style attribute 185 | this.setCss(document.body,"overflow") 186 | // or 187 | this.setCss(document.body,"overflow", "") 188 | ``` 189 | 190 | ### dynamicCss 191 | ```js 192 | // used to create a stylesheet and set rules in it. 193 | // adds a method: 194 | // setCssRules(newRules) 195 | // 196 | // usage: 197 | this.setCssRules({body: {overflow: "hidden"}}) 198 | // to remove a rule: 199 | this.setCssRules({body: {overflow: null}}) 200 | // nesting: 201 | this.setCssRules({body: {"& div": {width: "10px"},overflow:"hidden"}}) 202 | // is short for: (& will be replaced by the parent selector) 203 | // deeper nesting is allowed 204 | this.setCssRules({body: {overflow:"hidden"},"body div": {width: "10px"}}) 205 | ``` 206 | 207 | ### vue 208 | ```js 209 | // adds a computed property: 210 | // Vue 211 | // 212 | // usage: 213 | Vue = this.Vue 214 | ``` 215 | 216 | ### isOpened 217 | ```js 218 | // adds a boolean prop "isOpened" which will call "this.toggle()" on change 219 | // the state is saved on "opened" 220 | // 221 | // adds two methods: 222 | // setOpened(), setClosed() which will set "this.isOpened" without triggering 223 | // the toggle 224 | // and emit a event "toggled(opened)" 225 | // 226 | // usage: 227 | methods: 228 | toggle: function(){ 229 | if (this.opened) { 230 | this.close() 231 | } else { 232 | this.open() 233 | } 234 | } 235 | open: function() { 236 | this.setOpened() 237 | } 238 | close: function() { 239 | this.setClosed() 240 | } 241 | ``` 242 | ### parentListener 243 | ```js 244 | // adds two props: "ignoreParent" and "parent", which 245 | // defaults to "this.$el.parentElement" 246 | // 247 | // usage: 248 | methods: 249 | onParentClick: function() { 250 | // will be called when "ignoreParent" is false on click on parent 251 | } 252 | ``` 253 | ### fragToString 254 | ```js 255 | // adds a method: "fragToString" 256 | // usage: 257 | str = this.fragToString(someFrag) 258 | // str contains outerHTML of someFrag 259 | ``` 260 | ### class 261 | ```js 262 | // adds a computed property: "computedClass" 263 | // which merges a "mergeClass" data object/array and a "class" prop. 264 | // usage: 265 | template: "
", 266 | props: { 267 | class: { 268 | default: function() { 269 | return ["someClass"] 270 | } 271 | } 272 | }, 273 | data: function() { 274 | return { 275 | mergeClass: ["anotherClass"] 276 | } 277 | } 278 | // computedClass will be ["anotherClass","someClass"] when no prop is given 279 | // if the component is used like this 280 | // computedClass will be ["anotherClass","yetAnotherClass"] 281 | // works also with object notation and a mixture of both 282 | ``` 283 | ### style 284 | ```js 285 | // adds a computed property: "computedStyle" 286 | // which merges a "mergeStyle" data object and a "style" prop. 287 | // usage: 288 | template: "
", 289 | props: { 290 | style: { 291 | default: function() { 292 | return {color:"red"} 293 | } 294 | } 295 | }, 296 | data: function() { 297 | return { 298 | mergeStyle: {color:"blue"} 299 | } 300 | } 301 | // computedStyle will be [{color:"blue"},{color:"red"}] when no prop is given 302 | // if the component is used like this 303 | // computedStyle will be [{color:"blue"},{color:"white"}] 304 | // works also with array notation and a mixture of both 305 | ``` 306 | 307 | ### transition 308 | used to manage user provided transition in a reusable component 309 | ```js 310 | // adds a method: "processTransition" and a computed value "cTransition" 311 | // usage: 312 | template: "
", 313 | props: { 314 | transition: { 315 | type: String, 316 | default: "someDefaultTransition" 317 | } 318 | }, 319 | ready: function() { 320 | this.$on("before-enter",function(){ 321 | // will be called after element is inserted in dom but before transition starts 322 | // regardless of a optional provided transition 323 | }) 324 | } 325 | ``` 326 | `processTransition(name,parent = this.$parent)` resolves `name` to the actual transition on 327 | `parent` vm or global Vue. Adds `before-enter`, `after-enter`, `before-leave`, `after-leave`, `enterCancelled` and `leaveCancelled` emit hooks on the instance and inserts the modified transition into `this.$options.transitions[name]` 328 | `cTransition` lazily calls `processTransition` on the first transition and every time `transition` changes. 329 | 330 | You can disable transition by setting `this.disableTransition = true`. 331 | 332 | ### onMouseMove 333 | ```js 334 | // adds a method: onMouseMove(cb) which will return a function to dispose it 335 | // 336 | // usage: 337 | dispose = this.onMouseMove(function(){/*doSomethingOnMouseMove*/}) 338 | // remove your cb 339 | dispose() 340 | // all events will be automatically disposed on `beforeDestroy` 341 | ``` 342 | 343 | ## Develop 344 | Clone rep 345 | ``` 346 | npm install 347 | ``` 348 | Available scripts: 349 | ``` 350 | npm run build # compiles coffee-script in src/ 351 | npm run test # runs a single-run karma in chrome and firefox 352 | npm run watch # runs karma in chrome (uses src/*.coffee files direclty, no need for build) 353 | 354 | # to run only single tests: 355 | karma start --browsers Chrome --auto-watch --reporters spec --files ['test/onClick.coffee'] 356 | ``` 357 | 358 | ## License 359 | Copyright (c) 2015 Paul Pflugradt 360 | Licensed under the MIT license. 361 | -------------------------------------------------------------------------------- /build/index.coffee: -------------------------------------------------------------------------------- 1 | # out: ./index.js 2 | mixins = {} 3 | req = require.context("..",false,/.js$/) 4 | for name in [ 5 | "class" 6 | "dynamicCss" 7 | "fragToString" 8 | "getDocumentHeight" 9 | "getViewportOffset" 10 | "getViewportSize" 11 | "isOpened" 12 | "isOpened2" 13 | "onceDocument" 14 | "onClick" 15 | "onClickStack" 16 | "onClickStore" 17 | "onDocument" 18 | "onElementResize" 19 | "onMouseMove" 20 | "onWindowResize" 21 | "onWindowScroll" 22 | "parentListener" 23 | "setCss" 24 | "style" 25 | "transition" 26 | "vue" 27 | ] 28 | mixins[name] = req("./#{name}.js") 29 | if module.exports != null 30 | module.exports = mixins 31 | else 32 | window.vueMixins = mixins 33 | -------------------------------------------------------------------------------- /karma.conf.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (config) -> 2 | config.set 3 | preprocessors: "**/*.coffee": ["webpack",'sourcemap'] 4 | webpack: 5 | devtool: 'inline-source-map' 6 | resolve: 7 | extensions: [".js",".coffee"] 8 | module: 9 | loaders: [ 10 | { test: /\.coffee$/, loader: "coffee-loader" } 11 | ] 12 | webpackMiddleware: 13 | noInfo: true 14 | files: ["test/*.coffee"] 15 | frameworks: ["mocha","chai-dom","chai-spies","chai","vue-component"] 16 | plugins: [ 17 | require("karma-chai") 18 | require("karma-chai-dom") 19 | require("karma-chrome-launcher") 20 | require("karma-firefox-launcher") 21 | require("karma-mocha") 22 | require("karma-webpack") 23 | require("karma-sourcemap-loader") 24 | require("karma-spec-reporter") 25 | require("karma-chai-spies") 26 | require("karma-vue-component") 27 | ] 28 | browsers: ["Chromium","Firefox"] 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-mixins", 3 | "description": "collection of mixins in vue", 4 | "version": "0.3.6", 5 | "homepage": "https://github.com/paulpflug/", 6 | "author": { 7 | "name": "Paul Pflugradt", 8 | "email": "paul.pflugradt@gmail.com" 9 | }, 10 | "license": "MIT", 11 | "main": "build/bundle.js", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/paulpflug/vue-mixins" 15 | }, 16 | "engines": { 17 | "node": "*" 18 | }, 19 | "dependencies": { 20 | "lodash": "^4.16.1", 21 | "javascript-detect-element-resize": "^0.5.3" 22 | }, 23 | "devDependencies": { 24 | "chai": "^3.5.0", 25 | "chai-spies": "^0.7.1", 26 | "coffee-loader": "^0.7.2", 27 | "coffee-script": "^1.11.1", 28 | "karma": "^1.3.0", 29 | "karma-chai": "^0.1.0", 30 | "karma-chai-dom": "^1.1.0", 31 | "karma-chai-spies": "^0.1.4", 32 | "karma-chrome-launcher": "^2.0.0", 33 | "karma-firefox-launcher": "^1.0.0", 34 | "karma-mocha": "^1.1.1", 35 | "karma-sourcemap-loader": "^0.3.7", 36 | "karma-spec-reporter": "0.0.26", 37 | "karma-vue-component": "^0.1.0", 38 | "karma-webpack": "^1.8.0", 39 | "lodash": "^4.16.1", 40 | "mocha": "^3.0.2", 41 | "script-runner": "^0.1.5", 42 | "vue": "^1.0.26", 43 | "webpack": "^2.1.0-beta.25" 44 | }, 45 | "keywords": [], 46 | "readmeFilename": "README.md", 47 | "scripts": { 48 | "build:coffee": "coffee --no-header --compile --output . src/*.coffee", 49 | "build:webpack": "webpack", 50 | "build": "run-npm build:*", 51 | "watch": "karma start --browsers Chromium --auto-watch --reporters spec", 52 | "watch:coffee": "coffee --no-header --watch --output . src/*.coffee", 53 | "test": "karma start --single-run", 54 | "preversion": "npm test && npm run build", 55 | "version": "git add .", 56 | "postversion": "git push && git push --tags && npm publish" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/_throttledListener.coffee: -------------------------------------------------------------------------------- 1 | # out: ../_throttledListener.js 2 | called = [] 3 | fn = () -> called.push arguments 4 | if document? and window? 5 | document.addEventListener "DOMContentLoaded", -> 6 | rAF = window.requestAnimationFrame || 7 | window.mozRequestAnimationFrame || 8 | window.webkitRequestAnimationFrame || 9 | window.msRequestAnimationFrame 10 | 11 | cAF = window.cancelAnimationFrame || 12 | window.mozCancelAnimationFrame 13 | if rAF 14 | fn = (event, cb) -> 15 | lastRequest = null 16 | window.addEventListener event, -> 17 | args = arguments 18 | cAF(lastRequest) 19 | lastRequest = rAF -> cb.apply(null, args) 20 | else 21 | throttle = require("lodash/throttle") 22 | fn = (event, cb) -> 23 | window.addEventListener event, throttle(cb, 66) 24 | for args in called 25 | fn.apply(null, args) 26 | module.exports = fn 27 | -------------------------------------------------------------------------------- /src/class.coffee: -------------------------------------------------------------------------------- 1 | # out: ../class.js 2 | isString = (str) -> typeof str == 'string' or str instanceof String 3 | processInput = (obj) -> 4 | isArray = Array.isArray(obj) 5 | if isString(obj) and not isArray 6 | obj = obj.split(" ") 7 | isArray = true 8 | return isArray:isArray, obj:obj 9 | module.exports = 10 | computed: 11 | computedClass: -> 12 | { isArray: isArray1, obj: obj1 } = processInput(@class) 13 | return obj1 unless @mergeClass? 14 | { isArray: isArray2, obj: obj2 } = processInput(@mergeClass) 15 | if isArray1 and isArray2 16 | return obj2.concat obj1 17 | else 18 | result = {} 19 | if isArray2 20 | for item in obj2 21 | if isString(item) 22 | result[item] = true 23 | else 24 | for key,val of item 25 | result[key] = val 26 | else 27 | for key,val of obj2 28 | result[key] = val 29 | if isArray1 30 | for item in obj1 31 | if isString(item) 32 | result[item] = true 33 | else 34 | for key,val of item 35 | result[key] = val 36 | else 37 | for key,val of obj1 38 | result[key] = val 39 | return result 40 | -------------------------------------------------------------------------------- /src/dynamicCss.coffee: -------------------------------------------------------------------------------- 1 | # out: ../dynamicCss.js 2 | if document? 3 | styletag = document.createElement('style') 4 | styletag.setAttribute 'id', 'vm-dynamic-css' 5 | styletag.setAttribute "type","text/css" 6 | document.head.appendChild styletag 7 | stylesheet = if styletag.sheet then styletag.sheet else styletag.styleSheet 8 | 9 | flatten = (obj,base="") -> 10 | tmp = {} 11 | for key,val of obj 12 | if val instanceof Object 13 | props = {} 14 | for key2,val2 of val 15 | if key2.indexOf("&") > -1 16 | tmp2 = {} 17 | tmp2[key2.replace("&","")] = val2 18 | tmp2 = flatten(tmp2,base+key) 19 | for key3, val3 of tmp2 20 | tmp[key3] = val3 21 | else 22 | props[key2] = val2 23 | tmp[base+key] = props 24 | else 25 | tmp[base+key] = val 26 | return tmp 27 | 28 | setRules = (newRules) -> 29 | newRules = flatten(newRules) 30 | for selector,css of newRules 31 | str = "" 32 | {cssRule,index} = getRule(selector) 33 | if cssRule? 34 | if css? 35 | for singleRule,singleValue of css 36 | if singleValue? 37 | cssRule.style.setProperty singleRule, singleValue 38 | else 39 | cssRule.style.removeProperty singleRule 40 | else 41 | removeRule(index) 42 | else 43 | if css? 44 | for singleRule,singleValue of css 45 | if singleValue? 46 | str += singleRule+":"+singleValue+";" 47 | insertRule(selector,str) 48 | 49 | getRule = (selector) -> 50 | cssRules = stylesheet.cssRules or stylesheet.rules or [] 51 | for cssRule,index in cssRules 52 | if cssRule.selectorText == selector 53 | return {cssRule:cssRule,index:index} 54 | return {cssRule:null,index:null} 55 | 56 | insertRule = (selector,str) -> 57 | selector = selector.toLowerCase() 58 | str = str.toLowerCase() 59 | if stylesheet.insertRule 60 | stylesheet.insertRule selector + ' { ' + str + ' } ', stylesheet.cssRules.length 61 | else if stylesheet.addRule 62 | stylesheet.addRule selector, str 63 | 64 | removeRule = (index) -> 65 | if stylesheet.removeRule 66 | stylesheet.removeRule(index) 67 | else 68 | stylesheet.deleteRule(index) 69 | 70 | module.exports = 71 | methods: 72 | 'setCssRules': setRules 73 | -------------------------------------------------------------------------------- /src/fragToString.coffee: -------------------------------------------------------------------------------- 1 | # out: ../fragToString.js 2 | module.exports = 3 | methods: 4 | fragToString: (frag) -> 5 | div = document.createElement "div" 6 | div.appendChild frag.cloneNode(true) 7 | return div.innerHTML 8 | -------------------------------------------------------------------------------- /src/getDocumentHeight.coffee: -------------------------------------------------------------------------------- 1 | # out: ../getDocumentHeight.js 2 | 3 | getDocumentHeight = -> 4 | body = document.body 5 | html = document.documentElement 6 | return document.height or Math.max(body.scrollHeight, 7 | body.offsetHeight, 8 | html.clientHeight, 9 | html.scrollHeight, 10 | html.offsetHeight) 11 | 12 | module.exports = 13 | methods: 14 | getDocumentHeight: getDocumentHeight 15 | -------------------------------------------------------------------------------- /src/getViewportOffset.coffee: -------------------------------------------------------------------------------- 1 | # out: ../getViewportOffset.js 2 | # https://gist.github.com/jlong/eff01958791d3e0bf10c 3 | obj = require "./getViewportSize" 4 | getViewportSize = obj.methods.getViewportSize 5 | getViewportOffset = (element = @$el) -> 6 | left = element.offsetLeft 7 | top = element.offsetTop 8 | el = element.parentElement 9 | while el 10 | styles = getComputedStyle el 11 | if styles 12 | position = styles.getPropertyValue 'position' 13 | left -= el.scrollLeft 14 | top -= el.scrollTop 15 | if /relative|absolute|fixed/.test(position) 16 | left += parseInt(styles.getPropertyValue('border-left-width'), 10) 17 | top += parseInt(styles.getPropertyValue('border-top-width'), 10) 18 | left += el.offsetLeft 19 | top += el.offsetTop 20 | break if position == 'fixed' 21 | el = el.parentElement 22 | viewportSize = getViewportSize() 23 | return { 24 | top: top 25 | bottom: viewportSize.height-element.offsetHeight-top 26 | left: left 27 | right: viewportSize.width-element.offsetWidth-left 28 | } 29 | 30 | module.exports = 31 | methods: 32 | getViewportOffset: getViewportOffset 33 | -------------------------------------------------------------------------------- /src/getViewportSize.coffee: -------------------------------------------------------------------------------- 1 | # out: ../getViewportSize.js 2 | # http://andylangton.co.uk/blog/development/get-viewport-size-width-and-height-javascript 3 | getViewportSize = -> 4 | if window.innerWidth? 5 | e = window 6 | a = 'inner' 7 | else 8 | a = 'client' 9 | e = document.documentElement || document.body 10 | return { width : e[ a+'Width' ] , height : e[ a+'Height' ] } 11 | 12 | module.exports = 13 | methods: 14 | getViewportSize: getViewportSize 15 | -------------------------------------------------------------------------------- /src/getVue.coffee: -------------------------------------------------------------------------------- 1 | # out: ../getVue.js 2 | module.exports = 3 | methods: 4 | 'getVue': -> 5 | return Object.getPrototypeOf(Object.getPrototypeOf(@)).constructor 6 | -------------------------------------------------------------------------------- /src/isOpened.coffee: -------------------------------------------------------------------------------- 1 | # out: ../isOpened.js 2 | module.exports = 3 | props: 4 | "isOpened": 5 | type: Boolean 6 | default: false 7 | data: -> 8 | opened: null 9 | methods: 10 | setOpened: -> 11 | @opened = true 12 | @isOpened = true 13 | @$emit "toggled", true 14 | setClosed: -> 15 | @opened = false 16 | @isOpened = false 17 | @$emit "toggled", false 18 | watch: 19 | "isOpened": (val) -> 20 | if val != @opened 21 | @toggle() 22 | ready: -> 23 | if @isOpened 24 | @disableTransition = true 25 | @toggle() 26 | @disableTransition = false 27 | -------------------------------------------------------------------------------- /src/isOpened2.coffee: -------------------------------------------------------------------------------- 1 | # out: ../isOpened.js 2 | module.exports = 3 | props: 4 | "isOpened": 5 | type: Boolean 6 | default: false 7 | data: -> 8 | opened: null 9 | methods: 10 | setOpened: -> 11 | @opened = true 12 | @$emit "toggled", true 13 | setClosed: -> 14 | @opened = false 15 | @$emit "toggled", false 16 | watch: 17 | "isOpened": (val) -> 18 | if val != @opened 19 | @toggle() 20 | mounted: -> @$nextTick -> 21 | if @isOpened 22 | @toggle() 23 | -------------------------------------------------------------------------------- /src/onClick.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onClick.js 2 | module.exports = 3 | data: -> 4 | onClick: null 5 | methods: 6 | click: (e) -> 7 | if @onClick? 8 | @onClick(e) 9 | -------------------------------------------------------------------------------- /src/onClickStack.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onClickStack.js 2 | module.exports = 3 | data: -> 4 | onClickStack: [] 5 | methods: 6 | addToClickStack: (cb) -> 7 | @onClickStack.push(cb) unless @onClickStack.indexOf(cb) > -1 8 | return => 9 | index = @onClickStack.indexOf cb 10 | if index > -1 11 | @onClickStack.splice index,1 12 | click: (e) -> 13 | if @onClickStack.length > 0 14 | @onClickStack[@onClickStack.length-1](e) 15 | -------------------------------------------------------------------------------- /src/onClickStore.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onClickStore.js 2 | module.exports = 3 | data: -> 4 | onClickStore: [] 5 | methods: 6 | onClick: (cb) -> 7 | unless @onClickStore.indexOf(cb) > -1 8 | @onClickStore.push cb 9 | return => 10 | index = @onClickStore.indexOf cb 11 | if index > -1 12 | @onClickStore.splice index,1 13 | click: (e) -> 14 | for fn in @onClickStore 15 | fn(e) 16 | -------------------------------------------------------------------------------- /src/onDocument.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onDocument.js 2 | onDocument = (event, cb, useCapture) -> 3 | document.documentElement.addEventListener event, cb, useCapture 4 | remover = -> 5 | document.documentElement.removeEventListener event, cb 6 | remover = null 7 | return remover 8 | module.exports = 9 | methods: 10 | onDocument: onDocument 11 | -------------------------------------------------------------------------------- /src/onElementResize.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onElementResize.js 2 | called = [] 3 | disposes = [] 4 | fn = () -> 5 | args = arguments 6 | called.push args 7 | return -> 8 | i = called.indexOf args 9 | if i > -1 10 | called.splice i,1 11 | if disposes[i]? 12 | disposes[i]() 13 | disposes.splice i,1 14 | 15 | 16 | if document? and window? 17 | document.addEventListener "DOMContentLoaded", -> 18 | if window.MutationObserver? 19 | allResizeCbs = [] 20 | fn = (el, cb) -> 21 | elheight = el.offsetHeight 22 | elwidth = el.offsetWidth 23 | cbwrapper = -> 24 | if elheight != el.offsetHeight or elwidth != el.offsetWidth 25 | elheight = el.offsetHeight 26 | elwidth = el.offsetWidth 27 | cb.apply(null,arguments) 28 | allResizeCbs.push cbwrapper 29 | return -> 30 | index = allResizeCbs.indexOf cbwrapper 31 | if index > -1 32 | allResizeCbs.splice index,1 33 | callResizeCbs = -> 34 | for cb in allResizeCbs 35 | cb.apply(null,arguments) 36 | require("./_throttledListener")("resize",callResizeCbs) 37 | throttle = require("lodash/throttle") 38 | observer = new MutationObserver throttle(callResizeCbs,66) 39 | observer.observe document.body, 40 | attributes: true 41 | childList: true 42 | characterData: true 43 | subtree: true 44 | else 45 | require "javascript-detect-element-resize" 46 | fn = (el, cb) -> 47 | window.addResizeListener(el,cb) 48 | return -> window.removeResizeListener(el,cb) 49 | for args,i in called 50 | disposes[i] = fn.apply(null, args) 51 | module.exports = 52 | data: -> 53 | resizeCbDisposables: [] 54 | methods: 55 | onElementResize: (el,cb) -> 56 | return unless cb? 57 | dispose = fn(el,cb) 58 | @resizeCbDisposables.push dispose 59 | return => 60 | index = @resizeCbDisposables.indexOf dispose 61 | if index > -1 62 | @resizeCbDisposables.splice index,1 63 | dispose() 64 | 65 | beforeDestroy: -> 66 | for resizeCbDisposable in @resizeCbDisposables 67 | resizeCbDisposable() 68 | -------------------------------------------------------------------------------- /src/onMouseMove.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onMouseMove.js 2 | allMouseMoveCbs = [] 3 | callMouseMoveCbs = -> 4 | for cb in allMouseMoveCbs 5 | cb.apply(null,arguments) 6 | require("./_throttledListener")("mousemove",callMouseMoveCbs) 7 | module.exports = 8 | data: -> 9 | mouseMoveCbDisposables: [] 10 | methods: 11 | onMouseMove: (cb) -> 12 | allMouseMoveCbs.push cb 13 | dispose = -> 14 | index = allMouseMoveCbs.indexOf cb 15 | if index > -1 16 | allMouseMoveCbs.splice index,1 17 | @mouseMoveCbDisposables.push dispose 18 | return => 19 | dispose() 20 | index = @mouseMoveCbDisposables.indexOf dispose 21 | if index > -1 22 | @mouseMoveCbDisposables.splice index,1 23 | 24 | beforeDestroy: -> 25 | for mouseMoveCbDisposable in @mouseMoveCbDisposables 26 | mouseMoveCbDisposable() 27 | -------------------------------------------------------------------------------- /src/onResize.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onResize.js 2 | if window 3 | resizeRunning = false 4 | allResizeCbs = [] 5 | resizeHandler = -> 6 | unless resizeRunning 7 | resizeRunning = true 8 | if window.requestAnimationFrame 9 | window.requestAnimationFrame callResizeCbs 10 | else 11 | setTimeout callResizeCbs, 66 12 | callResizeCbs = (e) -> 13 | for cb in allResizeCbs 14 | cb(e) 15 | resizeRunning = false 16 | window.addEventListener "resize", resizeHandler 17 | observer = new MutationObserver resizeHandler 18 | observer.observe document.body, 19 | attributes: true 20 | childList: true 21 | characterData: true 22 | subtree: true 23 | 24 | module.exports = 25 | data: -> 26 | resizeCbDisposables: [] 27 | methods: 28 | addResizeCb: (cb) -> 29 | allResizeCbs.push cb 30 | dispose = -> 31 | index = allResizeCbs.indexOf cb 32 | if index > -1 33 | allResizeCbs.splice index,1 34 | @resizeCbDisposables.push dispose 35 | return => 36 | dispose() 37 | index = @resizeCbDisposables.indexOf dispose 38 | if index > -1 39 | @resizeCbDisposables.splice index,1 40 | 41 | beforeDestroy: -> 42 | for resizeCbDisposable in @resizeCbDisposables 43 | resizeCbDisposable() 44 | -------------------------------------------------------------------------------- /src/onWindowResize.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onWindowResize.js 2 | allResizeCbs = [] 3 | callResizeCbs = -> 4 | for cb in allResizeCbs 5 | cb.apply(null,arguments) 6 | resizeRunning = false 7 | require("./_throttledListener")("resize",callResizeCbs) 8 | 9 | module.exports = 10 | data: -> 11 | resizeCbDisposables: [] 12 | methods: 13 | onWindowResize: (cb) -> 14 | allResizeCbs.push cb 15 | dispose = -> 16 | index = allResizeCbs.indexOf cb 17 | if index > -1 18 | allResizeCbs.splice index,1 19 | @resizeCbDisposables.push dispose 20 | return => 21 | dispose() 22 | index = @resizeCbDisposables.indexOf dispose 23 | if index > -1 24 | @resizeCbDisposables.splice index,1 25 | 26 | beforeDestroy: -> 27 | for resizeCbDisposable in @resizeCbDisposables 28 | resizeCbDisposable() 29 | -------------------------------------------------------------------------------- /src/onWindowScroll.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onWindowScroll.js 2 | allScrollCbs = [] 3 | callScrollCbs = -> 4 | for cb in allScrollCbs 5 | cb.apply(null,arguments) 6 | require("./_throttledListener")("scroll",callScrollCbs) 7 | 8 | 9 | module.exports = 10 | data: -> 11 | scrollCbDisposables: [] 12 | methods: 13 | onWindowScroll: (cb) -> 14 | allScrollCbs.push cb 15 | dispose = -> 16 | index = allScrollCbs.indexOf cb 17 | if index > -1 18 | allScrollCbs.splice index,1 19 | @scrollCbDisposables.push dispose 20 | return => 21 | dispose() 22 | index = @scrollCbDisposables.indexOf dispose 23 | if index > -1 24 | @scrollCbDisposables.splice index,1 25 | 26 | beforeDestroy: -> 27 | for scrollCbDisposable in @scrollCbDisposables 28 | scrollCbDisposable() 29 | -------------------------------------------------------------------------------- /src/onceDocument.coffee: -------------------------------------------------------------------------------- 1 | # out: ../onceDocument.js 2 | onceDocument = (event, cb, useCapture) -> 3 | remover = null 4 | wrapper = (e) -> 5 | if cb(e) 6 | remover() if remover? 7 | document.documentElement.addEventListener event, wrapper, useCapture 8 | remover = -> 9 | document.documentElement.removeEventListener event, wrapper 10 | remover = null 11 | return remover 12 | module.exports = 13 | methods: 14 | onceDocument: onceDocument 15 | -------------------------------------------------------------------------------- /src/parentListener.coffee: -------------------------------------------------------------------------------- 1 | # out: ../parentListener.js 2 | module.exports = 3 | props: 4 | "ignoreParent": 5 | type: Boolean 6 | default: false 7 | "parent": 8 | type: Object 9 | data: -> 10 | removeParentClickListener: null 11 | methods: 12 | setupParent: (parent = @parent) -> 13 | unless @ignoreParent 14 | @removeParentClickListener?() 15 | parent.addEventListener "click", @onParentClick 16 | @removeParentClickListener = => 17 | parent.removeEventListener "click", @onParentClick 18 | watch: 19 | "parent": "setupParent" 20 | attached: -> 21 | unless @parent? 22 | @parent = @$el.parentElement 23 | else 24 | @setupParent() 25 | 26 | dettached: -> 27 | @removeParentClickListener?() 28 | -------------------------------------------------------------------------------- /src/parentListener2.coffee: -------------------------------------------------------------------------------- 1 | # out: ../parentListener.js 2 | module.exports = 3 | props: 4 | "ignoreParent": 5 | type: Boolean 6 | default: false 7 | "parent": 8 | type: Object 9 | data: -> 10 | removeParentClickListener: null 11 | methods: 12 | setupParent: (parent = @parent) -> 13 | unless @ignoreParent 14 | @removeParentClickListener?() 15 | parent.addEventListener "click", @onParentClick 16 | @removeParentClickListener = => 17 | parent.removeEventListener "click", @onParentClick 18 | watch: 19 | "parent": "setupParent" 20 | mounted: -> @$nextTick -> 21 | unless @parent? 22 | @parent = @$el.parentElement 23 | else 24 | @setupParent() 25 | 26 | beforeDestroy: -> 27 | @removeParentClickListener?() 28 | -------------------------------------------------------------------------------- /src/setCss.coffee: -------------------------------------------------------------------------------- 1 | # out: ../setCss.js 2 | setCss = (el, name, value) -> 3 | return unless el? and name? 4 | style = el.getAttribute("style") 5 | css = null 6 | if style? 7 | splitted = style.split(";") 8 | css = {} 9 | for str in splitted 10 | if str 11 | [cssname,cssvalue] = str.split(":") 12 | cssname = cssname.slice(1) if cssname[0] == ' ' 13 | css[cssname] = cssvalue 14 | if value? and value != "" 15 | css[name] = value 16 | else if css[name]? 17 | delete css[name] 18 | else if value? and value != "" 19 | css = {} 20 | css[name] = value 21 | cssString = "" 22 | if css? 23 | for name,value of css 24 | cssString += "#{name}:#{value};" 25 | if cssString 26 | el.setAttribute("style", cssString) 27 | else 28 | el.removeAttribute("style") 29 | module.exports = 30 | methods: 31 | setCss: setCss 32 | -------------------------------------------------------------------------------- /src/style.coffee: -------------------------------------------------------------------------------- 1 | # out: ../style.js 2 | isString = (str) -> typeof str == 'string' or str instanceof String 3 | trim = (str) -> return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '') 4 | arrayize = (arr) -> 5 | if Array.isArray(arr) 6 | return arr 7 | else if isString(arr) 8 | obj = {} 9 | for opt in arr.split(";") 10 | kv = opt.split(":") 11 | obj[trim(kv[0])] = trim(kv[1]) 12 | return [obj] 13 | else 14 | return [arr] 15 | module.exports = 16 | computed: 17 | computedStyle: -> 18 | style = arrayize(@style) 19 | return style unless @mergeStyle? 20 | return arrayize(@mergeStyle).concat style 21 | -------------------------------------------------------------------------------- /src/transition.coffee: -------------------------------------------------------------------------------- 1 | # out: ../transition.js 2 | module.exports = 3 | mixins: [ 4 | require("./vue") 5 | ] 6 | computed: 7 | cTransition: -> 8 | if @disableTransition 9 | name = "empty" 10 | else 11 | name = @transition 12 | @processTransition(name) 13 | return name 14 | methods: 15 | processTransition: (value, parent = @$parent) -> 16 | unless value == "empty" 17 | hooks = @Vue.util.resolveAsset(parent.$options,'transitions',value) 18 | unless hooks? 19 | hooks ?= @$options.transitions[value] 20 | return if hooks?.modified 21 | if hooks? 22 | newHooks = 23 | enterClass: hooks.enterClass 24 | leaveClass: hooks.leaveClass 25 | leave: hooks.leave 26 | beforeEnter: hooks.beforeEnter 27 | else 28 | newHooks = {} 29 | newHooks.modified = true 30 | addHook = (name) => 31 | eventName = @Vue.util.hyphenate(name) 32 | if hooks?[name]? 33 | hook = hooks[name] 34 | fn = (el) => 35 | @$emit eventName 36 | hook.bind(@)(el) 37 | else 38 | fn = (el) => 39 | @$emit eventName 40 | newHooks[name] = fn 41 | for hook in ["afterEnter","beforeLeave","afterLeave","enterCancelled","leaveCancelled"] 42 | addHook(hook) 43 | if hooks?.enter? and hooks.enter.length == 2 44 | hook = hooks.enter 45 | newHooks.enter = (el,cb) => 46 | @$emit "before-enter" 47 | hook.bind(@)(el,cb) 48 | else if hooks?.enter? 49 | hook = hooks.enter 50 | newHooks.enter = (el) => 51 | @$emit "before-enter" 52 | hook.bind(@)(el) 53 | else 54 | newHooks.enter = (el) => @$emit "before-enter" 55 | @$options.transitions[value] = newHooks 56 | -------------------------------------------------------------------------------- /src/transition2.coffee: -------------------------------------------------------------------------------- 1 | # out: ../transition.js 2 | module.exports = 3 | mixins: [ 4 | require("./vue") 5 | ] 6 | computed: 7 | cTransition: -> 8 | return @processTransition(@transition) 9 | cTransitionGroup: -> 10 | return @processTransitionGroup(@transition) 11 | methods: 12 | processTransitionGroup: (name, options = {}) -> 13 | options.defaultName ?= "transition-group" 14 | @processTransition name, options 15 | processTransition: (name, options = {}) -> 16 | {parent=@$parent, defaultName="transition"} = options 17 | parent ?= @ 18 | comp = @Vue.util.resolveAsset(parent.$options,'components',name) 19 | if parent != @ and not comp? 20 | comp = @Vue.util.resolveAsset(@$options,'components',name) 21 | if comp? 22 | @$options.components[name] = comp 23 | else 24 | name = defaultName 25 | return name 26 | -------------------------------------------------------------------------------- /src/vue.coffee: -------------------------------------------------------------------------------- 1 | # out: ../vue.js 2 | module.exports = 3 | computed: 4 | Vue: -> 5 | return Object.getPrototypeOf(Object.getPrototypeOf(@)).constructor 6 | -------------------------------------------------------------------------------- /test/class.coffee: -------------------------------------------------------------------------------- 1 | comp = 2 | el: -> document.createElement "div" 3 | template: "
" 4 | mixins: [require "../src/class.coffee"] 5 | data: -> 6 | mergeClass: [] 7 | props: 8 | class: 9 | default: -> [] 10 | 11 | comp = Vue.extend(comp) 12 | comp = new comp() 13 | 14 | describe "class", -> 15 | it 'should have class', -> 16 | should.exist comp.class 17 | comp.class.length.should.equal 0 18 | it 'should have mergeClass', -> 19 | should.exist comp.mergeClass 20 | comp.mergeClass.length.should.equal 0 21 | it 'should have computedClass', -> 22 | should.exist comp.computedClass 23 | comp.computedClass.length.should.equal 0 24 | it 'should merge arrays', -> 25 | comp.class = ["a"] 26 | comp.mergeClass = ["b"] 27 | comp.computedClass[0].should.equal "b" 28 | comp.computedClass[1].should.equal "a" 29 | it "should convert to object, when one isn't a array", -> 30 | comp.class = a:true 31 | comp.mergeClass = ["b"] 32 | comp.computedClass.a.should.be.true 33 | comp.computedClass.b.should.be.true 34 | comp.class = ["c"] 35 | comp.mergeClass = d:true 36 | comp.computedClass.c.should.be.true 37 | comp.computedClass.d.should.be.true 38 | should.not.exist comp.computedClass.a 39 | should.not.exist comp.computedClass.b 40 | it "should merge objects", -> 41 | comp.class = a:true,b:true 42 | comp.mergeClass = b:false,c:true 43 | comp.computedClass.a.should.be.true 44 | comp.computedClass.b.should.be.true 45 | comp.computedClass.c.should.be.true 46 | it "should support objects in arrays", -> 47 | comp.class = ["a", b:false] 48 | comp.mergeClass = b:true,c:true 49 | comp.computedClass.a.should.be.true 50 | comp.computedClass.b.should.be.false 51 | comp.computedClass.c.should.be.true 52 | it "should support plain class strings", -> 53 | comp.class = "a b" 54 | comp.mergeClass = "c d e" 55 | comp.computedClass.length.should.equal 5 56 | comp.computedClass[0].should.equal "c" 57 | comp.computedClass[1].should.equal "d" 58 | comp.computedClass[2].should.equal "e" 59 | comp.computedClass[3].should.equal "a" 60 | comp.computedClass[4].should.equal "b" 61 | -------------------------------------------------------------------------------- /test/dynamicCss.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/dynamicCss.coffee"] 2 | styletag = document.getElementById("vm-dynamic-css") 3 | stylesheet = if styletag.sheet then styletag.sheet else styletag.styleSheet 4 | cssRules = stylesheet.cssRules or stylesheet.rules or [] 5 | describe "dynamicCss", -> 6 | it 'should add a rule', -> 7 | comp.setCssRules "p":"font-size": "12px" 8 | cssRules[0].cssText.should.equal "p { font-size: 12px; }" 9 | it 'should add another property', -> 10 | comp.setCssRules "p":"height": "12px" 11 | cssRules[0].cssText.should.equal "p { font-size: 12px; height: 12px; }" 12 | it 'should remove a property', -> 13 | comp.setCssRules "p":"height": null 14 | cssRules[0].cssText.should.equal "p { font-size: 12px; }" 15 | it 'should remove a rule', -> 16 | comp.setCssRules "p":null 17 | should.not.exist(cssRules[0]) 18 | it 'should flatten', -> 19 | comp.setCssRules "p": {"& p" :{"font-size": "12px"},"height":"12px"} 20 | cssRules[0].cssText.should.equal "p p { font-size: 12px; }" 21 | cssRules[1].cssText.should.equal "p { height: 12px; }" 22 | it 'should remove flattened', -> 23 | comp.setCssRules "p": "& p": null 24 | comp.setCssRules "p": null 25 | should.not.exist(cssRules[0]) 26 | should.not.exist(cssRules[1]) 27 | it 'should flatten deep', -> 28 | comp.setCssRules "p": {"& p" :{"& p" :{"font-size": "12px"}}} 29 | cssRules = stylesheet.cssRules or stylesheet.rules or [] 30 | cssRules[0].cssText.should.equal "p p p { font-size: 12px; }" 31 | -------------------------------------------------------------------------------- /test/fragToString.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/fragToString.coffee"] 2 | describe "fragToString", -> 3 | it "should resolve a frag to string", -> 4 | frag = document.createDocumentFragment() 5 | p = document.createElement('p') 6 | p.textContent = 'test' 7 | frag.appendChild p 8 | comp.fragToString(frag).should.equal("

test

") 9 | -------------------------------------------------------------------------------- /test/getViewportOffset.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp { 2 | template: "
" 3 | mixins: [require "../src/getViewportOffset.coffee"] 4 | } 5 | describe "getViewportOffset", -> 6 | it 'should return a object with top and left', -> 7 | obj = comp.getViewportOffset() 8 | should.exist(obj.top) 9 | should.exist(obj.left) 10 | -------------------------------------------------------------------------------- /test/getViewportSize.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/getViewportSize.coffee"] 2 | describe "getViewportSize", -> 3 | it 'should return a object with width and height', -> 4 | obj = comp.getViewportSize() 5 | should.exist(obj.width) 6 | should.exist(obj.height) 7 | -------------------------------------------------------------------------------- /test/getVue.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/getVue.coffee"] 2 | describe "getVue", -> 3 | it 'should get Vue', -> 4 | name = comp.getVue().toString() 5 | name = name.substr('function '.length); 6 | name = name.substr(0, name.indexOf('(')); 7 | name.should.equal "Vue" 8 | -------------------------------------------------------------------------------- /test/onClick.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onClick.coffee"] 2 | describe "onClick", -> 3 | it 'should be called on click', -> 4 | spy = chai.spy() 5 | comp.onClick = spy 6 | comp.click() 7 | spy.should.have.been.called.once 8 | -------------------------------------------------------------------------------- /test/onClickStack.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onClickStack.coffee"] 2 | describe "onClickStack", -> 3 | disposes = [] 4 | spies = [chai.spy(),chai.spy(),chai.spy()] 5 | it 'should call last cb on click', -> 6 | for spy in spies 7 | disposes.push comp.addToClickStack spy 8 | comp.click() 9 | spies[0].should.not.have.been.called 10 | spies[1].should.not.have.been.called 11 | spies[2].should.have.been.called.once 12 | it 'should properly dispose', -> 13 | disposes[2]() 14 | comp.click() 15 | spies[0].should.not.have.been.called 16 | spies[1].should.have.been.called.once 17 | disposes[1]() 18 | comp.click() 19 | spies[0].should.have.been.called.once 20 | -------------------------------------------------------------------------------- /test/onClickStore.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onClickStore.coffee"] 2 | describe "onClickStore", -> 3 | disposes = [] 4 | spies = [chai.spy(),chai.spy(),chai.spy()] 5 | it 'should call all cbs on click', -> 6 | for spy in spies 7 | disposes.push comp.onClick spy 8 | comp.click() 9 | spies[0].should.have.been.called.once 10 | spies[1].should.have.been.called.once 11 | spies[2].should.have.been.called.once 12 | it 'should properly dispose', -> 13 | disposes[2]() 14 | comp.click() 15 | spies[0].should.have.been.called.twice 16 | spies[1].should.have.been.called.twice 17 | spies[2].should.have.been.called.once 18 | disposes[1]() 19 | comp.click() 20 | spies[0].should.have.been.called.exactly(3) 21 | spies[1].should.have.been.called.twice 22 | spies[2].should.have.been.called.once 23 | -------------------------------------------------------------------------------- /test/onDocument.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onDocument.coffee"] 2 | describe "onDocument", -> 3 | dispose = null 4 | it 'should call cb on event', -> 5 | spy = chai.spy() 6 | dispose = comp.onDocument "click", spy 7 | document.documentElement.dispatchEvent(new Event("click")) 8 | spy.should.have.been.called.once 9 | it 'should properly dispose cb', -> 10 | spy = chai.spy() 11 | dispose() 12 | document.documentElement.dispatchEvent(new Event("click")) 13 | spy.should.not.have.been.called 14 | -------------------------------------------------------------------------------- /test/onElementResize.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onElementResize.coffee"] 2 | cel = comp.$el 3 | describe "onElementResize", -> 4 | it "should not call cb on window resize "+ 5 | " when element doesn't change", (done) -> 6 | spy = chai.spy() 7 | comp.onElementResize cel, spy 8 | document.dispatchEvent( 9 | new Event 'resize', 10 | 'view': window 11 | 'bubbles': true 12 | 'cancelable': true 13 | ) 14 | window.requestAnimationFrame -> 15 | spy.should.not.have.been.called() 16 | done() 17 | 18 | it "should call cb on element width change", (done) -> 19 | spy = chai.spy() 20 | comp.onElementResize cel, spy 21 | cel.setAttribute("style", "width:200px") 22 | setTimeout (-> 23 | spy.should.have.been.called.once 24 | cel.removeAttribute("style") 25 | setTimeout (-> 26 | spy.should.have.been.called.twice 27 | done() 28 | ),100 29 | ),100 30 | it "should call cb on element height change", (done) -> 31 | spy = chai.spy() 32 | comp.onElementResize cel, spy 33 | cel.setAttribute("style", "height:200px") 34 | setTimeout (-> 35 | spy.should.have.been.called.once 36 | cel.removeAttribute("style") 37 | setTimeout (-> 38 | spy.should.have.been.called.twice 39 | done() 40 | ),100 41 | ),100 42 | it "should call cb on element when an "+ 43 | "larger inner element is added", (done) -> 44 | spy = chai.spy() 45 | comp.onElementResize cel, spy 46 | el = document.createElement "div" 47 | el.setAttribute "style", "height:200px" 48 | cel.appendChild el 49 | setTimeout (-> 50 | spy.should.have.been.called.once 51 | cel.removeChild el 52 | setTimeout (-> 53 | spy.should.have.been.called.twice 54 | done() 55 | ),100 56 | ),100 57 | -------------------------------------------------------------------------------- /test/onMouseMove.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onMouseMove.coffee"] 2 | describe "onMouseMove", -> 3 | it 'should call cb on mouse move', (done) -> 4 | spy = chai.spy() 5 | comp.onMouseMove spy 6 | document.dispatchEvent( 7 | new Event 'mousemove', 8 | 'view': window 9 | 'bubbles': true 10 | 'cancelable': true 11 | ) 12 | window.requestAnimationFrame -> 13 | spy.should.have.been.called.once 14 | done() 15 | -------------------------------------------------------------------------------- /test/onWindowResize.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onWindowResize.coffee"] 2 | describe "onWindowResize", -> 3 | it 'should call cb on window resize', (done) -> 4 | spy = chai.spy() 5 | comp.onWindowResize spy 6 | document.dispatchEvent( 7 | new Event 'resize', 8 | 'view': window 9 | 'bubbles': true 10 | 'cancelable': true 11 | ) 12 | window.requestAnimationFrame -> 13 | spy.should.have.been.called.once 14 | done() 15 | -------------------------------------------------------------------------------- /test/onceDocument.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/onceDocument.coffee"] 2 | describe "onceDocument", -> 3 | dispose = null 4 | it 'should call cb on event and remove on success', -> 5 | spy = chai.spy() 6 | comp.onceDocument "click", -> spy(); return true 7 | document.documentElement.dispatchEvent(new Event("click")) 8 | spy.should.have.been.called.once 9 | document.documentElement.dispatchEvent(new Event("click")) 10 | spy.should.have.been.called.once 11 | it 'should call cb on event and not remove on failure', -> 12 | spy = chai.spy() 13 | dispose = comp.onceDocument "click", -> spy(); return false 14 | document.documentElement.dispatchEvent(new Event("click")) 15 | spy.should.have.been.called.once 16 | document.documentElement.dispatchEvent(new Event("click")) 17 | spy.should.have.been.called.twice 18 | it 'should properly dispose cb', -> 19 | spy = chai.spy() 20 | dispose() 21 | document.documentElement.dispatchEvent(new Event("click")) 22 | spy.should.not.have.been.called 23 | -------------------------------------------------------------------------------- /test/setCss.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/setCss.coffee"] 2 | describe "setCss", -> 3 | it 'should add a style prop', -> 4 | comp.setCss document.body, "width", "100%" 5 | document.body.should.have.attr("style","width:100%;") 6 | it 'should add another style prop', -> 7 | comp.setCss document.body, "height", "100%" 8 | document.body.should.have.attr("style","width:100%;height:100%;") 9 | it 'should remove a style prop', -> 10 | comp.setCss document.body, "width" 11 | document.body.should.have.attr("style","height:100%;") 12 | it 'should remove another style prop', -> 13 | comp.setCss document.body, "height" 14 | document.body.should.not.have.attr("style") 15 | -------------------------------------------------------------------------------- /test/style.coffee: -------------------------------------------------------------------------------- 1 | comp = 2 | el: -> document.createElement "div" 3 | template: "
" 4 | mixins: [require "../src/style.coffee"] 5 | data: -> 6 | mergeStyle: [] 7 | props: 8 | style: 9 | default: -> [] 10 | 11 | comp = Vue.extend(comp) 12 | comp = new comp() 13 | 14 | describe "style", -> 15 | it 'should have style', -> 16 | should.exist comp.style 17 | comp.style.length.should.equal 0 18 | it 'should have mergeStyle', -> 19 | should.exist comp.mergeStyle 20 | comp.mergeStyle.length.should.equal 0 21 | it 'should have computedStyle', -> 22 | should.exist comp.computedStyle 23 | comp.computedStyle.length.should.equal 0 24 | it 'should merge arrays', -> 25 | comp.style = ["a"] 26 | comp.mergeStyle = ["b"] 27 | comp.computedStyle[0].should.equal "b" 28 | comp.computedStyle[1].should.equal "a" 29 | it "should convert to array, when one isn't a array", -> 30 | comp.style = a:true 31 | comp.mergeStyle = ["b"] 32 | comp.computedStyle.length.should.equal 2 33 | comp.computedStyle[0].should.equal "b" 34 | comp.computedStyle[1].a.should.be.true 35 | comp.style = ["c"] 36 | comp.mergeStyle = d:true 37 | comp.computedStyle.length.should.equal 2 38 | comp.computedStyle[0].d.should.be.true 39 | comp.computedStyle[1].should.equal "c" 40 | it "should support plain style strings", -> 41 | comp.style = "color:red" 42 | comp.mergeStyle = "color:blue;font-size:10px" 43 | comp.computedStyle.length.should.equal 2 44 | comp.computedStyle[0].color.should.equal "blue" 45 | comp.computedStyle[0]["font-size"].should.equal "10px" 46 | comp.computedStyle[1].color.should.equal "red" 47 | -------------------------------------------------------------------------------- /test/vue.coffee: -------------------------------------------------------------------------------- 1 | comp = loadComp mixins: [require "../src/vue.coffee"] 2 | describe "vue", -> 3 | it 'should get Vue', -> 4 | name = comp.Vue.toString() 5 | name = name.substr('function '.length); 6 | name = name.substr(0, name.indexOf('(')); 7 | name.should.equal "Vue" 8 | -------------------------------------------------------------------------------- /webpack.config.coffee: -------------------------------------------------------------------------------- 1 | webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: "./build/index.js" 5 | output: 6 | filename: "./build/bundle.js" 7 | plugins: [ 8 | new webpack.optimize.UglifyJsPlugin compress: warnings: false 9 | ] 10 | } 11 | --------------------------------------------------------------------------------