├── .babelrc ├── .bowerrc ├── .csscomb.json ├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .release.json ├── .stylelintrc.json ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bower.json ├── config.js ├── dist ├── css │ ├── asScrollable.css │ └── asScrollable.min.css ├── jquery-asScrollable.es.js ├── jquery-asScrollable.js ├── jquery-asScrollable.min.js └── jquery-asScrollable.min.js.map ├── examples ├── absolute.html ├── css │ ├── main.css │ └── normalize.css ├── fixed.html ├── index.html └── js │ ├── holder.js │ ├── jquery-asScrollbar.js │ └── jquery.js ├── gulp ├── tasks │ ├── archive.js │ ├── assets.js │ ├── browser.js │ ├── clean.js │ ├── deploy.js │ ├── lint-scripts.js │ ├── lint-styles.js │ ├── release.js │ ├── scripts.js │ ├── styles.js │ └── test.js └── util │ ├── getFolders.js │ ├── getSrcFiles.js │ └── handleErrors.js ├── gulpfile.babel.js ├── karma.conf.js ├── manifest.json ├── package.json └── src ├── asScrollable.js ├── defaults.js ├── info.js ├── main.js ├── scss └── asScrollable.scss └── util.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.csscomb.json: -------------------------------------------------------------------------------- 1 | { 2 | "remove-empty-rulesets": true, 3 | "always-semicolon": true, 4 | "color-case": "lower", 5 | "block-indent": " ", 6 | "color-shorthand": true, 7 | "element-case": "lower", 8 | "eof-newline": true, 9 | "leading-zero": false, 10 | "quotes": "double", 11 | "space-before-colon": "", 12 | "space-after-colon": " ", 13 | "space-before-combinator": " ", 14 | "space-after-combinator": " ", 15 | "space-between-declarations": "\n", 16 | "space-before-opening-brace": " ", 17 | "space-after-opening-brace": "\n", 18 | "space-after-selector-delimiter": " ", 19 | "space-before-selector-delimiter": "", 20 | "space-before-closing-brace": "\n", 21 | "strip-spaces": true, 22 | "tab-size": true, 23 | "unitless-zero": true, 24 | "vendor-prefix-align": false, 25 | "sort-order": [ 26 | "position", 27 | "top", 28 | "right", 29 | "bottom", 30 | "left", 31 | "z-index", 32 | "-moz-box-sizing", 33 | "-webkit-box-sizing", 34 | "box-sizing", 35 | "display", 36 | "flex", 37 | "flex-align", 38 | "flex-basis", 39 | "flex-direction", 40 | "flex-flow", 41 | "flex-grow", 42 | "flex-order", 43 | "flex-pack", 44 | "float", 45 | "width", 46 | "min-width", 47 | "max-width", 48 | "height", 49 | "min-height", 50 | "max-height", 51 | "padding", 52 | "padding-top", 53 | "padding-right", 54 | "padding-bottom", 55 | "padding-left", 56 | "margin", 57 | "margin-top", 58 | "margin-right", 59 | "margin-bottom", 60 | "margin-left", 61 | "overflow", 62 | "overflow-x", 63 | "overflow-y", 64 | "-webkit-overflow-scrolling", 65 | "-ms-overflow-x", 66 | "-ms-overflow-y", 67 | "-ms-overflow-style", 68 | "clip", 69 | "clear", 70 | "font", 71 | "font-family", 72 | "font-size", 73 | "font-style", 74 | "font-weight", 75 | "font-variant", 76 | "font-size-adjust", 77 | "font-stretch", 78 | "font-effect", 79 | "font-emphasize", 80 | "font-emphasize-position", 81 | "font-emphasize-style", 82 | "font-smooth", 83 | "-webkit-hyphens", 84 | "-moz-hyphens", 85 | "hyphens", 86 | "line-height", 87 | "color", 88 | "text-align", 89 | "-webkit-text-align-last", 90 | "-moz-text-align-last", 91 | "-ms-text-align-last", 92 | "text-align-last", 93 | "text-emphasis", 94 | "text-emphasis-color", 95 | "text-emphasis-style", 96 | "text-emphasis-position", 97 | "text-decoration", 98 | "text-indent", 99 | "text-justify", 100 | "text-outline", 101 | "-ms-text-overflow", 102 | "text-overflow", 103 | "text-overflow-ellipsis", 104 | "text-overflow-mode", 105 | "text-shadow", 106 | "text-transform", 107 | "text-wrap", 108 | "-webkit-text-size-adjust", 109 | "-ms-text-size-adjust", 110 | "letter-spacing", 111 | "-ms-word-break", 112 | "word-break", 113 | "word-spacing", 114 | "-ms-word-wrap", 115 | "word-wrap", 116 | "-moz-tab-size", 117 | "-o-tab-size", 118 | "tab-size", 119 | "white-space", 120 | "vertical-align", 121 | "list-style", 122 | "list-style-position", 123 | "list-style-type", 124 | "list-style-image", 125 | "pointer-events", 126 | "-ms-touch-action", 127 | "touch-action", 128 | "cursor", 129 | "visibility", 130 | "zoom", 131 | "table-layout", 132 | "empty-cells", 133 | "caption-side", 134 | "border-spacing", 135 | "border-collapse", 136 | "content", 137 | "quotes", 138 | "counter-reset", 139 | "counter-increment", 140 | "resize", 141 | "-webkit-user-select", 142 | "-moz-user-select", 143 | "-ms-user-select", 144 | "-o-user-select", 145 | "user-select", 146 | "nav-index", 147 | "nav-up", 148 | "nav-right", 149 | "nav-down", 150 | "nav-left", 151 | "background", 152 | "background-color", 153 | "background-image", 154 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 155 | "filter:progid:DXImageTransform.Microsoft.gradient", 156 | "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", 157 | "filter", 158 | "background-repeat", 159 | "background-attachment", 160 | "background-position", 161 | "background-position-x", 162 | "background-position-y", 163 | "-moz-background-clip", 164 | "-webkit-background-clip", 165 | "background-clip", 166 | "background-origin", 167 | "-moz-background-size", 168 | "-o-background-size", 169 | "-webkit-background-size", 170 | "background-size", 171 | "border", 172 | "border-color", 173 | "border-style", 174 | "border-width", 175 | "border-top", 176 | "border-top-color", 177 | "border-top-style", 178 | "border-top-width", 179 | "border-right", 180 | "border-right-color", 181 | "border-right-style", 182 | "border-right-width", 183 | "border-bottom", 184 | "border-bottom-color", 185 | "border-bottom-style", 186 | "border-bottom-width", 187 | "border-left", 188 | "border-left-color", 189 | "border-left-style", 190 | "border-left-width", 191 | "-moz-border-radius", 192 | "-o-border-radius", 193 | "-webkit-border-radius", 194 | "border-radius", 195 | "border-top-left-radius", 196 | "border-top-right-radius", 197 | "border-bottom-right-radius", 198 | "border-bottom-left-radius", 199 | "-moz-border-image", 200 | "-o-border-image", 201 | "-webkit-border-image", 202 | "border-image", 203 | "-moz-border-image-source", 204 | "-o-border-image-source", 205 | "-webkit-border-image-source", 206 | "border-image-source", 207 | "-moz-border-image-slice", 208 | "-o-border-image-slice", 209 | "-webkit-border-image-slice", 210 | "border-image-slice", 211 | "-moz-border-image-width", 212 | "-o-border-image-width", 213 | "-webkit-border-image-width", 214 | "border-image-width", 215 | "-moz-border-image-outset", 216 | "-o-border-image-outset", 217 | "-webkit-border-image-outset", 218 | "border-image-outset", 219 | "-moz-border-image-repeat", 220 | "-o-border-image-repeat", 221 | "-webkit-border-image-repeat", 222 | "border-image-repeat", 223 | "outline", 224 | "outline-width", 225 | "outline-style", 226 | "outline-color", 227 | "outline-offset", 228 | "-webkit-box-shadow", 229 | "-moz-box-shadow", 230 | "box-shadow", 231 | "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", 232 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 233 | "opacity", 234 | "-ms-interpolation-mode", 235 | "-moz-transition", 236 | "-ms-transition", 237 | "-o-transition", 238 | "-webkit-transition", 239 | "transition", 240 | "-moz-transition-delay", 241 | "-ms-transition-delay", 242 | "-o-transition-delay", 243 | "-webkit-transition-delay", 244 | "transition-delay", 245 | "-moz-transition-timing-function", 246 | "-ms-transition-timing-function", 247 | "-o-transition-timing-function", 248 | "-webkit-transition-timing-function", 249 | "transition-timing-function", 250 | "-moz-transition-duration", 251 | "-ms-transition-duration", 252 | "-o-transition-duration", 253 | "-webkit-transition-duration", 254 | "transition-duration", 255 | "-moz-transition-property", 256 | "-ms-transition-property", 257 | "-o-transition-property", 258 | "-webkit-transition-property", 259 | "transition-property", 260 | "-moz-transform", 261 | "-ms-transform", 262 | "-o-transform", 263 | "-webkit-transform", 264 | "transform", 265 | "-moz-transform-origin", 266 | "-ms-transform-origin", 267 | "-o-transform-origin", 268 | "-webkit-transform-origin", 269 | "transform-origin", 270 | "-webkit-animation", 271 | "-moz-animation", 272 | "-ms-animation", 273 | "-o-animation", 274 | "animation", 275 | "-moz-animation-name", 276 | "-ms-animation-name", 277 | "-o-animation-name", 278 | "-webkit-animation-name", 279 | "animation-name", 280 | "-moz-animation-duration", 281 | "-ms-animation-duration", 282 | "-o-animation-duration", 283 | "-webkit-animation-duration", 284 | "animation-duration", 285 | "-moz-animation-play-state", 286 | "-ms-animation-play-state", 287 | "-o-animation-play-state", 288 | "-webkit-animation-play-state", 289 | "animation-play-state", 290 | "-moz-animation-timing-function", 291 | "-ms-animation-timing-function", 292 | "-o-animation-timing-function", 293 | "-webkit-animation-timing-function", 294 | "animation-timing-function", 295 | "-moz-animation-delay", 296 | "-ms-animation-delay", 297 | "-o-animation-delay", 298 | "-webkit-animation-delay", 299 | "animation-delay", 300 | "-moz-animation-iteration-count", 301 | "-ms-animation-iteration-count", 302 | "-o-animation-iteration-count", 303 | "-webkit-animation-iteration-count", 304 | "animation-iteration-count", 305 | "-moz-animation-direction", 306 | "-ms-animation-direction", 307 | "-o-animation-direction", 308 | "-webkit-animation-direction" 309 | ] 310 | } 311 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # End every file with a newline 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.{css,less,scss}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.{js,json}] 20 | indent_style = space 21 | indent_size = 2 22 | 23 | [*.{html,hbs}] 24 | indent_style = tab 25 | indent_size = 2 26 | 27 | [*.md] 28 | indent_style = space 29 | indent_size = 2 30 | insert_final_newline = false 31 | trim_trailing_whitespace = false 32 | 33 | [*.xml] 34 | indent_style = space 35 | indent_size = 2 36 | 37 | [*.yml] 38 | indent_style = space 39 | indent_size = 2 40 | 41 | [*.inc] 42 | indent_style = tab 43 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "env": { 5 | "browser": true, 6 | "es6": true, 7 | "jquery": true, 8 | "builtin": true, 9 | "mocha": true, 10 | "node": true 11 | }, 12 | "extends": "eslint:recommended", 13 | "parserOptions": { 14 | "ecmaVersion": 8, 15 | "sourceType": "module" 16 | }, 17 | // "plugins": [ 18 | // "prettier" 19 | // ], 20 | "rules": { 21 | // Prettier 22 | // "prettier/prettier": "error", 23 | // Possible Errors 24 | "no-inner-declarations": "off", 25 | "no-await-in-loop": "error", 26 | "no-compare-neg-zero": "error", 27 | "no-extra-parens": ["error", "all", { 28 | "returnAssign": false, 29 | "enforceForArrowConditionals": false 30 | } 31 | ], 32 | "no-prototype-builtins": "off", 33 | "no-template-curly-in-string": "error", 34 | "valid-jsdoc": "off", 35 | 36 | // Best Practices 37 | "accessor-pairs": "error", 38 | "array-callback-return": "error", 39 | "block-scoped-var": "error", 40 | "class-methods-use-this": "off", 41 | "complexity": "error", 42 | "consistent-return": "error", 43 | "curly": "error", 44 | "default-case": "error", 45 | "dot-location": ["error", "property"], 46 | "dot-notation": "error", 47 | "eqeqeq": "error", 48 | "guard-for-in": "error", 49 | "no-alert": "error", 50 | "no-caller": "error", 51 | "no-div-regex": "error", 52 | "no-else-return": "error", 53 | "no-empty-function": "error", 54 | "no-eq-null": "error", 55 | "no-eval": "error", 56 | "no-extend-native": "error", 57 | "no-extra-bind": "error", 58 | "no-extra-label": "error", 59 | "no-floating-decimal": "error", 60 | "no-implicit-coercion": "error", 61 | "no-implicit-globals": "error", 62 | "no-implied-eval": "error", 63 | "no-invalid-this": "off", 64 | "no-iterator": "error", 65 | "no-labels": "error", 66 | "no-lone-blocks": "error", 67 | "no-loop-func": "error", 68 | "no-magic-numbers": "off", 69 | "no-multi-spaces": ["error", { 70 | "exceptions": { 71 | "AssignmentExpression": true, 72 | "ArrowFunctionExpression": true, 73 | "CallExpression": true, 74 | "VariableDeclarator": true 75 | } 76 | } 77 | ], 78 | "no-multi-str": "error", 79 | "no-new": "error", 80 | "no-new-func": "error", 81 | "no-new-wrappers": "error", 82 | "no-octal-escape": "error", 83 | "no-param-reassign": "off", 84 | "no-proto": "error", 85 | "no-restricted-properties": "error", 86 | "no-return-assign": "off", 87 | "no-return-await": "error", 88 | "no-script-url": "error", 89 | "no-self-compare": "error", 90 | "no-sequences": "error", 91 | "no-throw-literal": "error", 92 | "no-unmodified-loop-condition": "error", 93 | "no-unused-expressions": "error", 94 | "no-useless-call": "error", 95 | "no-useless-concat": "error", 96 | "no-useless-escape": "error", 97 | "no-useless-return": "off", 98 | "no-void": "error", 99 | "no-warning-comments": "off", 100 | "no-with": "error", 101 | "prefer-promise-reject-errors": "error", 102 | "radix": "error", 103 | "require-await": "error", 104 | "vars-on-top": "error", 105 | "wrap-iife": "error", 106 | "yoda": "error", 107 | 108 | // Strict Mode 109 | "strict": "error", 110 | 111 | // Variables 112 | "init-declarations": "off", 113 | "no-catch-shadow": "error", 114 | "no-label-var": "error", 115 | "no-restricted-globals": "error", 116 | "no-shadow": "off", 117 | "no-shadow-restricted-names": "error", 118 | "no-undef-init": "error", 119 | "no-undefined": "off", 120 | "no-use-before-define": "off", 121 | 122 | // Node.js and CommonJS 123 | "callback-return": "off", 124 | "global-require": "error", 125 | "handle-callback-err": "error", 126 | "no-mixed-requires": "error", 127 | "no-new-require": "error", 128 | "no-path-concat": "error", 129 | "no-process-env": "error", 130 | "no-process-exit": "error", 131 | "no-restricted-modules": "error", 132 | "no-sync": "error", 133 | 134 | // Stylistic Issues 135 | "array-bracket-spacing": "error", 136 | "block-spacing": "error", 137 | "brace-style": "error", 138 | "camelcase": "error", 139 | "capitalized-comments": "off", 140 | "comma-dangle": "error", 141 | "comma-spacing": "error", 142 | "comma-style": "error", 143 | "computed-property-spacing": "error", 144 | "consistent-this": "error", 145 | "eol-last": "error", 146 | "func-call-spacing": "error", 147 | "func-name-matching": "error", 148 | "func-names": "off", 149 | "func-style": ["error", "declaration", { 150 | "allowArrowFunctions": true 151 | }], 152 | "id-blacklist": "error", 153 | "id-length": "off", 154 | "id-match": "error", 155 | "indent": "off", 156 | "indent-legacy": ["error", 2, { "SwitchCase": 1 }], 157 | "jsx-quotes": "error", 158 | "key-spacing": "off", 159 | "keyword-spacing": "error", 160 | "line-comment-position": "off", 161 | "linebreak-style": ["error", "unix"], 162 | "lines-around-comment": "off", 163 | "lines-around-directive": "error", 164 | "max-depth": ["error", 10], 165 | "max-len": "off", 166 | "max-lines": "off", 167 | "max-nested-callbacks": "error", 168 | "max-params": "off", 169 | "max-statements": "off", 170 | "max-statements-per-line": "error", 171 | "multiline-ternary": "off", 172 | "new-cap": ["error", { "capIsNewExceptionPattern": "$.*" }], 173 | "new-parens": "error", 174 | "newline-after-var": "off", 175 | "newline-before-return": "off", 176 | "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 5 }], 177 | "no-array-constructor": "error", 178 | "no-bitwise": "error", 179 | "no-continue": "off", 180 | "no-inline-comments": "off", 181 | "no-lonely-if": "error", 182 | "no-mixed-operators": "off", 183 | "no-multi-assign": "error", 184 | "no-multiple-empty-lines": "error", 185 | "no-negated-condition": "off", 186 | "no-nested-ternary": "error", 187 | "no-new-object": "error", 188 | "no-plusplus": "off", 189 | "no-restricted-syntax": "error", 190 | "no-tabs": "error", 191 | "no-ternary": "off", 192 | "no-trailing-spaces": "error", 193 | "no-underscore-dangle": "off", 194 | "no-unneeded-ternary": "error", 195 | "no-whitespace-before-property": "error", 196 | "nonblock-statement-body-position": "error", 197 | "object-curly-newline": ["error", { "minProperties": 1 }], 198 | "object-curly-spacing": ["error", "always"], 199 | "object-property-newline": "error", 200 | "one-var": ["error", "never"], 201 | "one-var-declaration-per-line": "error", 202 | "operator-assignment": "error", 203 | "operator-linebreak": "off", 204 | "padded-blocks": "off", 205 | "quote-props": ["error", "as-needed"], 206 | "quotes": ["error", "single", { "allowTemplateLiterals": true }], 207 | "require-jsdoc": "off", 208 | "semi": ["error", "always"], 209 | "semi-spacing": ["error", {"before": false, "after": true}], 210 | "sort-keys": "off", 211 | "sort-vars": "error", 212 | "space-before-blocks": "error", 213 | "space-before-function-paren": ["error", { 214 | "anonymous": "always", 215 | "named": "never" 216 | }], 217 | "space-in-parens": "error", 218 | "space-infix-ops": "error", 219 | "space-unary-ops": "error", 220 | "spaced-comment": "error", 221 | "template-tag-spacing": "error", 222 | "unicode-bom": "error", 223 | "wrap-regex": "off", 224 | 225 | // ECMAScript 6 226 | "arrow-body-style": "off", 227 | "arrow-parens": "error", 228 | "arrow-spacing": "error", 229 | "generator-star-spacing": "error", 230 | "no-confusing-arrow": ["error", { "allowParens": true }], 231 | "no-duplicate-imports": "error", 232 | "no-restricted-imports": "error", 233 | "no-useless-computed-key": "error", 234 | "no-useless-constructor": "error", 235 | "no-useless-rename": "error", 236 | "no-var": "error", 237 | "object-shorthand": "error", 238 | "prefer-arrow-callback": "error", 239 | "prefer-const": "error", 240 | "prefer-destructuring": "off", 241 | "prefer-numeric-literals": "error", 242 | "prefer-rest-params": "error", 243 | "prefer-spread": "error", 244 | "prefer-template": "error", 245 | "rest-spread-spacing": "error", 246 | "sort-imports": "off", 247 | "symbol-description": "error", 248 | "template-curly-spacing": "error", 249 | "yield-star-spacing": "error" 250 | } 251 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ################################## 2 | # project-specific.gitattributes # 3 | ################################## 4 | 5 | 6 | ########################### 7 | # documents.gitattributes # 8 | ########################### 9 | *.doc diff=astextplain 10 | *.docx diff=astextplain 11 | *.dot diff=astextplain 12 | *.pdf diff=astextplain 13 | *.rtf diff=astextplain 14 | *.css text eol=lf 15 | *.scss text eol=lf 16 | *.less text eol=lf 17 | *.html text eol=lf 18 | *.js text eol=lf 19 | *.json text eol=lf 20 | *.md text eol=lf 21 | *.txt text eol=lf 22 | *.hbs text eol=lf 23 | *.mustache text eol=lf 24 | *.xml text eol=lf 25 | *.csv text eol=lf 26 | *.tab text eol=lf 27 | *.tsv text eol=lf 28 | *.sql text eol=lf 29 | 30 | ############################ 31 | ### graphics.gitattributes # 32 | ############################ 33 | *.png binary 34 | *.jpg binary 35 | *.jpeg binary 36 | *.gif binary 37 | *.tif binary 38 | *.tiff binary 39 | *.ico binary 40 | *.svg binary 41 | *.eps binary 42 | *.psd binary 43 | *.ai binary 44 | 45 | ########################### 46 | # git-crypt.gitattributes # 47 | ########################### 48 | vault_* binary filter=git-crypt diff=git-crypt 49 | vault.* binary filter=git-crypt diff=git-crypt 50 | 51 | ####################### 52 | # shell.gitattributes # 53 | ####################### 54 | # Linux 55 | *.sh text eol=lf 56 | 57 | # Windows 58 | *.bat text eol=crlf 59 | *.cmd text eol=crlf 60 | *.ps1 text eol=crlf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | _[Remove this line and all of the above before submitting your issue]_ 7 | 8 | ### Checklist 9 | * [ ] I'm using **version** [x.x.x] 10 | * [ ] My **browser** is: 11 | * [ ] This is a **Sass** issue: I'm using version [x.x.x] 12 | * [ ] I am sure this issue is **not a duplicate**? 13 | 14 | ### Description 15 | 16 | [Description of the bug, enhancement, or question] 17 | [Please tag accordingly] 18 | 19 | ### How can we reproduce this bug? 20 | 21 | 1. [First Step] 22 | 2. [Second Step] 23 | 3. [and so on...] 24 | 25 | ### What did you expect to happen? 26 | 27 | ### What happened instead? 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | _[Remove this line and all of the above before submitting your PR]_ 15 | 16 | ### Pull Request 17 | 18 | Fixes # 19 | 20 | Changes proposed: 21 | 22 | * [ ] Add 23 | * [ ] Fix 24 | * [ ] Remove 25 | * [ ] Update 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #################### 2 | # project-specific # 3 | #################### 4 | 5 | 6 | #################### 7 | # Generated source # 8 | #################### 9 | _build/ 10 | archives/ 11 | results 12 | screenshots 13 | 14 | ########################## 15 | # Dependency directories # 16 | ########################## 17 | bower_components 18 | node_modules 19 | 20 | ####### 21 | # OSX # 22 | ####### 23 | .DS_Store 24 | .AppleDouble 25 | .LSOverride 26 | 27 | # Icon must end with two \r 28 | Icon 29 | 30 | # Thumbnails 31 | ._* 32 | 33 | # Files that might appear in the root of a volume 34 | .DocumentRevisions-V100 35 | .fseventsd 36 | .Spotlight-V100 37 | .TemporaryItems 38 | .Trashes 39 | .VolumeIcon.icns 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ########### 49 | # Windows # 50 | ########### 51 | # Windows image file caches 52 | Thumbs.db 53 | ehthumbs.db 54 | 55 | # Folder config file 56 | Desktop.ini 57 | 58 | # Recycle Bin used on file shares 59 | $RECYCLE.BIN/ 60 | 61 | # Windows Installer files 62 | *.cab 63 | *.msi 64 | *.msm 65 | *.msp 66 | 67 | # Windows shortcuts 68 | *.lnk 69 | 70 | ######## 71 | # Node # 72 | ######## 73 | # Logs 74 | logs 75 | *.log 76 | npm-debug.log* 77 | yarn.lock 78 | # Runtime data 79 | pids 80 | *.pid 81 | *.seed 82 | 83 | # Directory for instrumented libs generated by jscoverage/JSCover 84 | lib-cov 85 | 86 | # Coverage directory used by tools like istanbul 87 | coverage 88 | 89 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 90 | .grunt 91 | 92 | # Sass 93 | .sass-cache/ 94 | *.css.map 95 | 96 | # node-waf configuration 97 | .lock-wscript 98 | 99 | # Compiled binary addons (http://nodejs.org/api/addons.html) 100 | build/Release 101 | 102 | # Dependency directory 103 | node_modules 104 | 105 | # JSPM 106 | jspm_packages 107 | 108 | # Optional npm cache directory 109 | .npm 110 | 111 | ############### 112 | # SublimeText # 113 | ############### 114 | # cache files for sublime text 115 | *.tmlanguage.cache 116 | *.tmPreferences.cache 117 | *.stTheme.cache 118 | 119 | # workspace files are user-specific 120 | *.sublime-workspace 121 | 122 | # project files should be checked into the repository, unless a significant 123 | # proportion of contributors will probably not be using SublimeText 124 | # *.sublime-project 125 | 126 | # sftp configuration file 127 | sftp-config.json 128 | 129 | ####### 130 | # Vim # 131 | ####### 132 | # swap 133 | [._]*.s[a-w][a-z] 134 | [._]s[a-w][a-z] 135 | # persistent undo 136 | *.un~ 137 | # session 138 | Session.vim 139 | # temporary 140 | .netrwhist 141 | *~ 142 | 143 | ############# 144 | # NotepadPP # 145 | ############## 146 | *.bak 147 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.xml 2 | yarn.lock 3 | package-lock.json 4 | .editorconfig 5 | .babelrc 6 | .beautifyrc 7 | .sass-cache 8 | .bowerrc 9 | .eslintrc.yml 10 | .github 11 | .gitattributes 12 | .release.json 13 | _build 14 | bower.json 15 | composer.json 16 | gulpfile.js 17 | gulpfile.babel.js 18 | README.md 19 | CONTRIBUTING.md 20 | coverage 21 | docs 22 | test 23 | gulp 24 | test 25 | demo 26 | libs 27 | archives 28 | config.js 29 | karma.conf.js 30 | bower_components 31 | -------------------------------------------------------------------------------- /.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": false, 3 | "force": false, 4 | "pkgFiles": ["package.json", "bower.json"], 5 | "increment": "patch", 6 | "commitMessage": "Release v%s", 7 | "tagName": "v%s", 8 | "tagAnnotation": "Release v%s", 9 | "buildCommand": "gulp build", 10 | "npm": { 11 | "publish": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-bootstrap" 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.10" 4 | - "6.2" 5 | - "stable" 6 | sudo: false 7 | 8 | script: 9 | - npm run test 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | addons: 16 | sauce_connect: true 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | Please take a moment to review this document in order to make the contribution 4 | process easy and effective for everyone involved. 5 | 6 | Following these guidelines helps to communicate that you respect the time of 7 | the developers managing and developing this open source project. In return, 8 | they should reciprocate that respect in addressing your issue or assessing 9 | patches and features. 10 | 11 | 12 | ## Using the issue tracker 13 | 14 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 15 | [features requests](#feature-requests) and submitting pull requests, but please 16 | respect the following restrictions: 17 | 18 | * Please **do not** use the issue tracker for personal support requests (use 19 | [Stack Overflow](http://stackoverflow.com) or IRC). 20 | 21 | * Please **do not** derail or troll issues. Keep the discussion on topic and 22 | respect the opinions of others. 23 | 24 | 25 | ## Bug reports 26 | 27 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 28 | Good bug reports are extremely helpful - thank you! 29 | 30 | Guidelines for bug reports: 31 | 32 | 1. **Use the GitHub issue search.** Check if the issue has already been 33 | reported. 34 | 35 | 2. **Check if the issue has been fixed.** Try to reproduce it using the 36 | latest `master` or development branch in the repository. 37 | 38 | 3. **Provide environment details.** Provide your operating system, browser(s), 39 | devices, and jquery-asScrollbar version. 40 | 41 | 4. **Create an isolated and reproducible test case.** Create a [reduced test 42 | case](http://css-tricks.com/6263-reduced-test-cases/). 43 | 44 | 5. **Include a live example.** Make use of jsFiddle or jsBin to share your 45 | isolated test cases. 46 | 47 | A good bug report shouldn't leave others needing to chase you up for more 48 | information. Please try to be as detailed as possible in your report. What is 49 | your environment? What steps will reproduce the issue? What browser(s) and OS 50 | experience the problem? What would you expect to be the outcome? All these 51 | details will help people to fix any potential bugs. 52 | 53 | Example: 54 | 55 | > Short and descriptive example bug report title 56 | > 57 | > A summary of the issue and the browser/OS environment in which it occurs. If 58 | > suitable, include the steps required to reproduce the bug. 59 | > 60 | > 1. This is the first step 61 | > 2. This is the second step 62 | > 3. Further steps, etc. 63 | > 64 | > `` - a link to the reduced test case 65 | > 66 | > Any other information you want to share that is relevant to the issue being 67 | > reported. This might include the lines of code that you have identified as 68 | > causing the bug, and potential solutions (and your opinions on their 69 | > merits). 70 | 71 | 72 | ## Feature requests 73 | 74 | Feature requests are welcome. But take a moment to find out whether your idea 75 | fits with the scope and aims of the project. It's up to *you* to make a strong 76 | case to convince the project's developers of the merits of this feature. Please 77 | provide as much detail and context as possible. 78 | 79 | 80 | ## Pull Requests 81 | 82 | **Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) 83 | 84 | Good pull requests - patches, improvements, new features - are a fantastic 85 | help. They should remain focused in scope and avoid containing unrelated 86 | commits. 87 | 88 | **Please ask first** before embarking on any significant pull request (e.g. 89 | implementing features, refactoring code, porting to a different language), 90 | otherwise you risk spending a lot of time working on something that the 91 | project's developers might not want to merge into the project. 92 | 93 | Please adhere to the coding conventions used throughout a project (indentation, 94 | accurate comments, etc.) and any other requirements (such as test coverage). 95 | 96 | Follow this process if you'd like your work considered for inclusion in the 97 | project: 98 | 99 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 100 | and configure the remotes: 101 | 102 | ```bash 103 | # Clone your fork of the repo into the current directory 104 | git clone https://github.com// 105 | # Navigate to the newly cloned directory 106 | cd 107 | # Assign the original repo to a remote called "upstream" 108 | git remote add upstream https://github.com// 109 | ``` 110 | 111 | 2. If you cloned a while ago, get the latest changes from upstream: 112 | 113 | ```bash 114 | git checkout 115 | git pull upstream 116 | ``` 117 | 118 | 3. Create a new topic branch (off the main project development branch) to 119 | contain your feature, change, or fix: 120 | 121 | ```bash 122 | git checkout -b 123 | ``` 124 | 125 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 126 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 127 | or your code is unlikely be merged into the main project. Use Git's 128 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 129 | feature to tidy up your commits before making them public. 130 | 131 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 132 | 133 | ```bash 134 | git pull [--rebase] upstream 135 | ``` 136 | 137 | 6. Push your topic branch up to your fork: 138 | 139 | ```bash 140 | git push origin 141 | ``` 142 | 143 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 144 | with a clear title and description. 145 | 146 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to 147 | license your work under the same license as that used by the project. 148 | 149 | 150 | ## Code Consitency 151 | 152 | To help create consistent looking code throughout the project, we use a few tools to help us. 153 | 154 | #### ESlint 155 | We use [ESlint](http://eslint.org) on each build to find easy-to-catch errors and potential problems in our js. You can find our ESlint settings in the `.eslintrc.yml` file in the root of the project. 156 | 157 | #### EditorConfig 158 | 159 | We use [EditorConfig](http://EditorConfig.org) to maintain consistent coding styles between various editors and IDEs. You can find our settings in the `.editorconfig` file in the root of the project. 160 | 161 | 162 | ## Development 163 | 164 | We are using node, gulp and babel to build and (in the future) test this project. This means that you must setup a local development environment: 165 | 166 | 1. Install `node` and `npm` using your preferred method 167 | 2. Install the gulp CLI: `npm install -g gulp-cli` 168 | 3. Install the Babel CLI: `npm install -g babel-cli` 169 | 4. Install the project's development dependencies: `npm install` 170 | 171 | #### Available Tasks 172 | - `gulp` or `gulp watch` Start watch for changes and server with Browsersync. 173 | - `gulp build` Run all development tasks 174 | - `gulp serve` Start server with Browsersync. 175 | - `gulp clean` Clean output directories. 176 | - `gulp bundler` Bundle javasript modules. 177 | - `gulp scripts` Concatenate and minify JavaScript to `dist`. 178 | - `gulp lint:script` Lint ES6 files using eslint. 179 | - `gulp clean` Clean out distribution javascript files. 180 | - `gulp tdd` Test for Test Driven Development purposes. 181 | - `gulp test` Test for Continuous Integration purposes. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [jQuery asScrollable](https://github.com/amazingSurge/jquery-asScrollable) ![bower][bower-image] [![NPM version][npm-image]][npm-url] [![Dependency Status][daviddm-image]][daviddm-url] [![prs-welcome]](#contributing) 2 | 3 | > A jquery plugin that make a block element scrollable. 4 | 5 | ## Table of contents 6 | - [Main files](#main-files) 7 | - [Quick start](#quick-start) 8 | - [Requirements](#requirements) 9 | - [Usage](#usage) 10 | - [Examples](#examples) 11 | - [Options](#options) 12 | - [Methods](#methods) 13 | - [Events](#events) 14 | - [No conflict](#no-conflict) 15 | - [Browser support](#browser-support) 16 | - [Contributing](#contributing) 17 | - [Development](#development) 18 | - [Changelog](#changelog) 19 | - [Copyright and license](#copyright-and-license) 20 | 21 | ## Main files 22 | ``` 23 | dist/ 24 | ├── jquery-asScrollable.js 25 | ├── jquery-asScrollable.es.js 26 | ├── jquery-asScrollable.min.js 27 | └── css/ 28 |    ├── asScrollable.css 29 |    └── asScrollable.min.css 30 | ``` 31 | 32 | ## Quick start 33 | Several quick start options are available: 34 | #### Download the latest build 35 | 36 | * [Development](https://raw.githubusercontent.com/amazingSurge/jquery-asScrollable/master/dist/jquery-asScrollable.js) - unminified 37 | * [Production](https://raw.githubusercontent.com/amazingSurge/jquery-asScrollable/master/dist/jquery-asScrollable.min.js) - minified 38 | 39 | #### Install From Bower 40 | ```sh 41 | bower install jquery-asScrollable --save 42 | ``` 43 | 44 | #### Install From Npm 45 | ```sh 46 | npm install jquery-asScrollable --save 47 | ``` 48 | 49 | #### Install From Yarn 50 | ```sh 51 | yarn add jquery-asScrollable 52 | ``` 53 | 54 | #### Build From Source 55 | If you want build from source: 56 | 57 | ```sh 58 | git clone git@github.com:amazingSurge/jquery-asScrollable.git 59 | cd jquery-asScrollable 60 | npm install 61 | npm install -g gulp-cli babel-cli 62 | gulp build 63 | ``` 64 | 65 | Done! 66 | 67 | ## Requirements 68 | `jquery-asScrollable` requires the latest version of [`jQuery`](https://jquery.com/download/), [`jquery-asScollbar`](https://github.com/amazingSurge/jquery-asScrollbar). 69 | 70 | ## Usage 71 | #### Including files: 72 | 73 | ```html 74 | 75 | 76 | 77 | 78 | ``` 79 | 80 | #### Required HTML structure 81 | 82 | ```html 83 |
84 | content here 85 |
86 | ``` 87 | 88 | #### Initialization 89 | All you need to do is call the plugin on the element: 90 | 91 | ```javascript 92 | jQuery(function($) { 93 | $('.example').asScrollable(); 94 | }); 95 | ``` 96 | 97 | ### Better Usage 98 | If we supply two wrap div for the content, the dom will not redraw which having better performances. 99 | 100 | ```html 101 |
102 |
103 |
104 | content here 105 |
106 |
107 |
108 | 109 | 117 | ``` 118 | 119 | ## Examples 120 | There are some example usages that you can look at to get started. They can be found in the 121 | [examples folder](https://github.com/amazingSurge/jquery-asScrollable/tree/master/examples). 122 | 123 | ## Options 124 | `jquery-asScrollable` can accept an options object to alter the way it behaves. You can see the default options by call `$.asScrollable.setDefaults()`. The structure of an options object is as follows: 125 | 126 | ``` 127 | { 128 | namespace: 'asScrollable', 129 | 130 | skin: null, 131 | 132 | contentSelector: null, 133 | containerSelector: null, 134 | 135 | enabledClass: 'is-enabled', 136 | disabledClass: 'is-disabled', 137 | 138 | draggingClass: 'is-dragging', 139 | hoveringClass: 'is-hovering', 140 | scrollingClass: 'is-scrolling', 141 | 142 | direction: 'vertical', // vertical, horizontal, both, auto 143 | 144 | showOnHover: true, 145 | showOnBarHover: false, 146 | 147 | duration: 500, 148 | easing: 'ease-in', // linear, ease, ease-in, ease-out, ease-in-out 149 | 150 | responsive: true, 151 | throttle: 20, 152 | 153 | scrollbar: {} 154 | } 155 | ``` 156 | 157 | ## Methods 158 | Methods are called on asScrollable instances through the asScrollable method itself. 159 | You can also save the instances to variable for further use. 160 | 161 | ```javascript 162 | // call directly 163 | $().asScrollable('destroy'); 164 | 165 | // or 166 | var api = $().data('asScrollable'); 167 | api.destroy(); 168 | ``` 169 | 170 | #### scrollTo(direction, position) 171 | Scroll the content to position in direction. 172 | ```javascript 173 | // scroll to 50px in vertical 174 | $().asScrollable('scrollTo', 'vertical', '50'); 175 | 176 | // scroll to 50% in horizontal 177 | $().asScrollable('scrollTo', 'horizontal', '50%'); 178 | ``` 179 | 180 | #### scrollBy(direction, size) 181 | Scroll the content by the size. 182 | ```javascript 183 | $().asScrollable('scrollBy', 'vertical', '10'); 184 | $().asScrollable('scrollBy', 'horizontal', '10%'); 185 | 186 | $().asScrollable('scrollBy', 'vertical', '-10'); 187 | $().asScrollable('scrollBy', 'horizontal', '-10%'); 188 | ``` 189 | 190 | #### enable() 191 | Enable the scrollable functions. 192 | ```javascript 193 | $().asScrollable('enable'); 194 | ``` 195 | 196 | #### disable() 197 | Disable the scrollable functions. 198 | ```javascript 199 | $().asScrollable('disable'); 200 | ``` 201 | 202 | #### destroy() 203 | Destroy the scrollable instance. 204 | ```javascript 205 | $().asScrollable('destroy'); 206 | ``` 207 | 208 | ## Events 209 | `jquery-asScrollable` provides custom events for the plugin’s unique actions. 210 | 211 | ```javascript 212 | $('.the-element').on('asScrollable::ready', function (e) { 213 | // on instance ready 214 | }); 215 | 216 | ``` 217 | 218 | Event | Description 219 | ------- | ----------- 220 | ready | Fires when the instance is ready for API use. 221 | enable | Fired when the `enable` instance method has been called. 222 | disable | Fired when the `disable` instance method has been called. 223 | destroy | Fires when an instance is destroyed. 224 | 225 | ## No conflict 226 | If you have to use other plugin with the same namespace, just call the `$.asScrollable.noConflict` method to revert to it. 227 | 228 | ```html 229 | 230 | 231 | 235 | ``` 236 | 237 | ## Browser support 238 | 239 | Tested on all major browsers. 240 | 241 | | Safari | Chrome | Firefox | Edge | IE | Opera | 242 | |:--:|:--:|:--:|:--:|:--:|:--:| 243 | | Latest ✓ | Latest ✓ | Latest ✓ | Latest ✓ | 9-11 ✓ | Latest ✓ | 244 | 245 | As a jQuery plugin, you also need to see the [jQuery Browser Support](http://jquery.com/browser-support/). 246 | 247 | ## Contributing 248 | Anyone and everyone is welcome to contribute. Please take a moment to 249 | review the [guidelines for contributing](CONTRIBUTING.md). Make sure you're using the latest version of `jquery-asScrollable` before submitting an issue. There are several ways to help out: 250 | 251 | * [Bug reports](CONTRIBUTING.md#bug-reports) 252 | * [Feature requests](CONTRIBUTING.md#feature-requests) 253 | * [Pull requests](CONTRIBUTING.md#pull-requests) 254 | * Write test cases for open bug issues 255 | * Contribute to the documentation 256 | 257 | ## Development 258 | `jquery-asScrollable` is built modularly and uses Gulp as a build system to build its distributable files. To install the necessary dependencies for the build system, please run: 259 | 260 | ```sh 261 | npm install -g gulp 262 | npm install -g babel-cli 263 | npm install 264 | ``` 265 | 266 | Then you can generate new distributable files from the sources, using: 267 | ``` 268 | gulp build 269 | ``` 270 | 271 | More gulp tasks can be found [here](CONTRIBUTING.md#available-tasks). 272 | 273 | ## Changelog 274 | To see the list of recent changes, see [Releases section](https://github.com/amazingSurge/jquery-asScrollable/releases). 275 | 276 | ## Copyright and license 277 | Copyright (C) 2016 amazingSurge. 278 | 279 | Licensed under [the LGPL license](LICENSE). 280 | 281 | [⬆ back to top](#table-of-contents) 282 | 283 | [bower-image]: https://img.shields.io/bower/v/jquery-asScrollable.svg?style=flat 284 | [bower-link]: https://david-dm.org/amazingSurge/jquery-asScrollable/dev-status.svg 285 | [npm-image]: https://badge.fury.io/js/jquery-asScrollable.svg?style=flat 286 | [npm-url]: https://npmjs.org/package/jquery-asScrollable 287 | [license]: https://img.shields.io/npm/l/jquery-asScrollable.svg?style=flat 288 | [prs-welcome]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg 289 | [daviddm-image]: https://david-dm.org/amazingSurge/jquery-asScrollable.svg?style=flat 290 | [daviddm-url]: https://david-dm.org/amazingSurge/jquery-asScrollable 291 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-asScrollable", 3 | "version": "0.4.10", 4 | "description": "A jquery plugin that make a block element scrollable.", 5 | "main": "dist/jquery-asScrollable.js", 6 | "copyright": "amazingSurge", 7 | "license": "LGPL-3.0", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "demo", 14 | "gulp", 15 | "CONTRIBUTING.md", 16 | "manifest.json", 17 | "karma.conf.js", 18 | "config.js", 19 | "package.json", 20 | "gulpfile.babel.js", 21 | "tests" 22 | ], 23 | "homepage": "https://github.com/amazingSurge/jquery-asScrollable", 24 | "authors": [ 25 | "amazingSurge " 26 | ], 27 | "moduleType": [ 28 | "globals" 29 | ], 30 | "dependencies": { 31 | "jquery": ">=2.2.0", 32 | "jquery-asScrollbar": "^0.5.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'graceful-fs'; 4 | import {argv} from 'yargs'; 5 | 6 | const production = argv.production || argv.prod || false; 7 | 8 | export default { 9 | getConfig: function(pkg, src, dest) { 10 | return { 11 | version: pkg.version, 12 | name: pkg.name, 13 | title: pkg.title, 14 | description: pkg.description, 15 | author: pkg.author, 16 | banner: `/** 17 | * ${pkg.title} v${pkg.version} 18 | * ${pkg.homepage} 19 | * 20 | * Copyright (c) ${pkg.author.name} 21 | * Released under the ${pkg.license} license 22 | */ 23 | `, 24 | // basic locations 25 | paths: { 26 | root: './', 27 | srcDir: `${src}/`, 28 | destDir: `${dest}/`, 29 | }, 30 | 31 | styles: { 32 | files: '**/*.scss', 33 | src: `${src}/scss`, 34 | dest: `${dest}/css`, 35 | prodSourcemap: false, 36 | sassIncludePaths: [], 37 | autoprefixer: { 38 | browsers: ['last 2 versions', 'ie >= 9', 'Android >= 2.3'] 39 | } 40 | }, 41 | 42 | scripts: { 43 | input: 'main.js', 44 | version: 'info.js', 45 | files: '**/*.js', 46 | src: `${src}`, 47 | dest: `${dest}`, 48 | prodSourcemap: false, 49 | test: './test', 50 | gulp: './gulp' 51 | }, 52 | 53 | archive: { 54 | src: `${dest}/**/*`, 55 | dest: './archives/', 56 | zip: {} 57 | }, 58 | 59 | browser: { 60 | baseDir: './', 61 | startPath: "examples/index.html", 62 | browserPort: 3000, 63 | UIPort: 3001, 64 | testPort: 3002, 65 | }, 66 | 67 | deploy: { 68 | versionFiles: ['package.json', 'bower.json'], 69 | increment: "patch", // major, minor, patch, premajor, preminor, prepatch, or prerelease. 70 | }, 71 | 72 | notify: { 73 | title: pkg.title 74 | }, 75 | 76 | env: 'development', 77 | production: production, 78 | setEnv: function(env) { 79 | if (typeof env !== 'string') return; 80 | this.env = env; 81 | this.production = env === 'production'; 82 | process.env.NODE_ENV = env; 83 | }, 84 | 85 | test: {}, 86 | }; 87 | }, 88 | 89 | init: function() { 90 | const pkg = JSON.parse(fs.readFileSync('./package.json', { encoding: 'utf-8' })); 91 | 92 | let src = 'src'; 93 | let dest = 'dist'; 94 | 95 | Object.assign(this, this.getConfig(pkg, src, dest, production)); 96 | this.setEnv(production? 'production': 'development'); 97 | 98 | return this; 99 | } 100 | 101 | }.init(); 102 | -------------------------------------------------------------------------------- /dist/css/asScrollable.css: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery asScrollable v0.4.10 3 | * https://github.com/amazingSurge/jquery-asScrollable 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | .asScrollable.is-enabled { 9 | overflow: hidden !important; 10 | } 11 | 12 | .is-enabled .asScrollable-container { 13 | -webkit-box-sizing: content-box !important; 14 | box-sizing: content-box !important; 15 | overflow: hidden !important; 16 | } 17 | 18 | .is-enabled .asScrollable-container::-webkit-scrollbar { 19 | width: 0; 20 | height: 0; 21 | 22 | -webkit-appearance: none; 23 | } 24 | 25 | .asScrollable-vertical.is-enabled .asScrollable-container { 26 | overflow-y: scroll !important; 27 | } 28 | 29 | .asScrollable-horizontal.is-enabled .asScrollable-container { 30 | overflow-x: scroll !important; 31 | } 32 | 33 | .is-enabled .asScrollable-content { 34 | position: relative !important; 35 | overflow: visible !important; 36 | } 37 | .is-enabled .asScrollable-content::before, .is-enabled .asScrollable-content::after { 38 | display: table; 39 | content: "\20"; 40 | } 41 | .is-enabled .asScrollable-content::after { 42 | clear: both; 43 | } 44 | 45 | .asScrollable-bar { 46 | position: absolute; 47 | right: 0; 48 | bottom: 0; 49 | -webkit-box-sizing: border-box; 50 | box-sizing: border-box; 51 | overflow: hidden; 52 | line-height: 0; 53 | -webkit-user-select: none; 54 | -moz-user-select: none; 55 | -ms-user-select: none; 56 | user-select: none; 57 | border-radius: 2px; 58 | -webkit-transition: opacity .5s; 59 | transition: opacity .5s; 60 | 61 | -webkit-touch-callout: none; 62 | user-input: disabled; 63 | user-focus: ignore; 64 | } 65 | .is-disabled .asScrollable-bar { 66 | display: none; 67 | } 68 | .asScrollable-bar-hide { 69 | opacity: 0; 70 | -webkit-transition-delay: 400ms; 71 | transition-delay: 400ms; 72 | } 73 | .asScrollable-bar.is-hovering { 74 | background: rgba(238, 238, 238, .4); 75 | } 76 | .asScrollable-bar.is-dragging { 77 | background: rgba(238, 238, 238, .6) !important; 78 | opacity: 1; 79 | } 80 | .asScrollable-bar.is-disabled { 81 | display: none; 82 | } 83 | .asScrollable-bar-handle { 84 | position: absolute; 85 | top: 0; 86 | left: 0; 87 | line-height: 0; 88 | cursor: pointer; 89 | background: rgba(224, 224, 224, .6); 90 | border-radius: 2px; 91 | -webkit-transition: width, height .5s; 92 | transition: width, height .5s; 93 | } 94 | .asScrollable-bar.is-dragging .asScrollable-bar-handle { 95 | background: rgba(150, 150, 150, .8) !important; 96 | } 97 | .asScrollable-bar.is-dragging, .asScrollable-bar.is-hovering { 98 | border-radius: 5px; 99 | } 100 | .asScrollable-bar.is-dragging .asScrollable-bar-handle, .asScrollable-bar.is-hovering .asScrollable-bar-handle { 101 | border-radius: 5px; 102 | } 103 | .asScrollable-bar-vertical { 104 | width: 4px; 105 | height: 100%; 106 | height: calc(100% - 10px); 107 | margin: 5px 3px; 108 | } 109 | .asScrollable-bar-vertical.is-dragging, .asScrollable-bar-vertical.is-hovering { 110 | width: 10px; 111 | margin: 5px 1px; 112 | } 113 | .asScrollable-bar-vertical .asScrollable-bar-handle { 114 | width: 100%; 115 | } 116 | .asScrollable-bar-horizontal { 117 | width: 100%; 118 | width: calc(100% - 10px); 119 | height: 4px; 120 | margin: 3px 5px; 121 | } 122 | .asScrollable-bar-horizontal.is-dragging, .asScrollable-bar-horizontal.is-hovering { 123 | height: 10px; 124 | margin: 1px 5px; 125 | } 126 | .asScrollable-bar-horizontal .asScrollable-bar-handle { 127 | height: 100%; 128 | } 129 | 130 | .asScrollable.is-scrolling .asScrollable-bar { 131 | opacity: 1; 132 | -webkit-transition: opacity 0; 133 | transition: opacity 0; 134 | } 135 | 136 | .asScrollable.is-hovering .asScrollable-bar-handle { 137 | background: rgba(150, 150, 150, .6); 138 | } 139 | 140 | .asScrollable.is-dragging { 141 | -webkit-user-select: none; 142 | -moz-user-select: none; 143 | -ms-user-select: none; 144 | user-select: none; 145 | 146 | -webkit-touch-callout: none; 147 | user-input: disabled; 148 | user-focus: ignore; 149 | } 150 | -------------------------------------------------------------------------------- /dist/css/asScrollable.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery asScrollable v0.4.10 3 | * https://github.com/amazingSurge/jquery-asScrollable 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | .asScrollable.is-enabled{overflow:hidden!important}.is-enabled .asScrollable-container{-webkit-box-sizing:content-box!important;box-sizing:content-box!important;overflow:hidden!important}.is-enabled .asScrollable-container::-webkit-scrollbar{width:0;height:0;-webkit-appearance:none}.asScrollable-vertical.is-enabled .asScrollable-container{overflow-y:scroll!important}.asScrollable-horizontal.is-enabled .asScrollable-container{overflow-x:scroll!important}.is-enabled .asScrollable-content{position:relative!important;overflow:visible!important}.is-enabled .asScrollable-content:after,.is-enabled .asScrollable-content:before{display:table;content:"\20"}.is-enabled .asScrollable-content:after{clear:both}.asScrollable-bar{position:absolute;right:0;bottom:0;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;line-height:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:2px;-webkit-transition:opacity .5s;transition:opacity .5s;-webkit-touch-callout:none;user-input:disabled;user-focus:ignore}.is-disabled .asScrollable-bar{display:none}.asScrollable-bar-hide{opacity:0;-webkit-transition-delay:.4s;transition-delay:.4s}.asScrollable-bar.is-hovering{background:hsla(0,0%,93%,.4)}.asScrollable-bar.is-dragging{background:hsla(0,0%,93%,.6)!important;opacity:1}.asScrollable-bar.is-disabled{display:none}.asScrollable-bar-handle{position:absolute;top:0;left:0;line-height:0;cursor:pointer;background:hsla(0,0%,88%,.6);border-radius:2px;-webkit-transition:width,height .5s;transition:width,height .5s}.asScrollable-bar.is-dragging .asScrollable-bar-handle{background:hsla(0,0%,59%,.8)!important}.asScrollable-bar.is-dragging,.asScrollable-bar.is-dragging .asScrollable-bar-handle,.asScrollable-bar.is-hovering,.asScrollable-bar.is-hovering .asScrollable-bar-handle{border-radius:5px}.asScrollable-bar-vertical{width:4px;height:100%;height:calc(100% - 10px);margin:5px 3px}.asScrollable-bar-vertical.is-dragging,.asScrollable-bar-vertical.is-hovering{width:10px;margin:5px 1px}.asScrollable-bar-vertical .asScrollable-bar-handle{width:100%}.asScrollable-bar-horizontal{width:100%;width:calc(100% - 10px);height:4px;margin:3px 5px}.asScrollable-bar-horizontal.is-dragging,.asScrollable-bar-horizontal.is-hovering{height:10px;margin:1px 5px}.asScrollable-bar-horizontal .asScrollable-bar-handle{height:100%}.asScrollable.is-scrolling .asScrollable-bar{opacity:1;-webkit-transition:opacity 0;transition:opacity 0}.asScrollable.is-hovering .asScrollable-bar-handle{background:hsla(0,0%,59%,.6)}.asScrollable.is-dragging{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;user-input:disabled;user-focus:ignore} 9 | /*# sourceMappingURL=asScrollable.min.css.map */ 10 | -------------------------------------------------------------------------------- /dist/jquery-asScrollable.es.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery asScrollable v0.4.10 3 | * https://github.com/amazingSurge/jquery-asScrollable 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | import $ from 'jquery'; 9 | 10 | var DEFAULTS = { 11 | namespace: 'asScrollable', 12 | 13 | skin: null, 14 | 15 | contentSelector: null, 16 | containerSelector: null, 17 | 18 | enabledClass: 'is-enabled', 19 | disabledClass: 'is-disabled', 20 | 21 | draggingClass: 'is-dragging', 22 | hoveringClass: 'is-hovering', 23 | scrollingClass: 'is-scrolling', 24 | 25 | direction: 'vertical', // vertical, horizontal, both, auto 26 | 27 | showOnHover: true, 28 | showOnBarHover: false, 29 | 30 | duration: 500, 31 | easing: 'ease-in', // linear, ease, ease-in, ease-out, ease-in-out 32 | 33 | responsive: true, 34 | throttle: 20, 35 | 36 | scrollbar: {} 37 | }; 38 | 39 | function getTime() { 40 | if (typeof window.performance !== 'undefined' && window.performance.now) { 41 | return window.performance.now(); 42 | } 43 | return Date.now(); 44 | } 45 | 46 | function isPercentage(n) { 47 | return typeof n === 'string' && n.indexOf('%') !== -1; 48 | } 49 | 50 | function conventToPercentage(n) { 51 | if (n < 0) { 52 | n = 0; 53 | } else if (n > 1) { 54 | n = 1; 55 | } 56 | return `${parseFloat(n).toFixed(4) * 100}%`; 57 | } 58 | 59 | function convertPercentageToFloat(n) { 60 | return parseFloat(n.slice(0, -1) / 100, 10); 61 | } 62 | 63 | let isFFLionScrollbar = (() => { 64 | 'use strict'; 65 | 66 | let isOSXFF, ua, version; 67 | ua = window.navigator.userAgent; 68 | isOSXFF = /(?=.+Mac OS X)(?=.+Firefox)/.test(ua); 69 | if (!isOSXFF) { 70 | return false; 71 | } 72 | version = /Firefox\/\d{2}\./.exec(ua); 73 | if (version) { 74 | version = version[0].replace(/\D+/g, ''); 75 | } 76 | return isOSXFF && +version > 23; 77 | })(); 78 | 79 | const NAMESPACE$1 = 'asScrollable'; 80 | 81 | let instanceId = 0; 82 | 83 | class AsScrollable { 84 | constructor(element, options) { 85 | this.$element = $(element); 86 | options = this.options = $.extend({}, DEFAULTS, options || {}, this.$element.data('options') || {}); 87 | 88 | this.classes = { 89 | wrap: options.namespace, 90 | content: `${options.namespace}-content`, 91 | container: `${options.namespace}-container`, 92 | bar: `${options.namespace}-bar`, 93 | barHide: `${options.namespace}-bar-hide`, 94 | skin: options.skin 95 | }; 96 | 97 | this.attributes = { 98 | vertical: { 99 | axis: 'Y', 100 | overflow: 'overflow-y', 101 | 102 | scroll: 'scrollTop', 103 | scrollLength: 'scrollHeight', 104 | pageOffset: 'pageYOffset', 105 | 106 | ffPadding: 'padding-right', 107 | 108 | length: 'height', 109 | clientLength: 'clientHeight', 110 | offset: 'offsetHeight', 111 | 112 | crossLength: 'width', 113 | crossClientLength: 'clientWidth', 114 | crossOffset: 'offsetWidth' 115 | }, 116 | horizontal: { 117 | axis: 'X', 118 | overflow: 'overflow-x', 119 | 120 | scroll: 'scrollLeft', 121 | scrollLength: 'scrollWidth', 122 | pageOffset: 'pageXOffset', 123 | 124 | ffPadding: 'padding-bottom', 125 | 126 | length: 'width', 127 | clientLength: 'clientWidth', 128 | offset: 'offsetWidth', 129 | 130 | crossLength: 'height', 131 | crossClientLength: 'clientHeight', 132 | crossOffset: 'offsetHeight' 133 | } 134 | }; 135 | 136 | // Current state information. 137 | this._states = {}; 138 | 139 | // Supported direction 140 | this.horizontal = null; 141 | this.vertical = null; 142 | 143 | this.$bar = null; 144 | 145 | // Current timeout 146 | this._frameId = null; 147 | this._timeoutId = null; 148 | 149 | this.instanceId = (++instanceId); 150 | 151 | this.easing = $.asScrollbar.getEasing(this.options.easing) || $.asScrollbar.getEasing('ease'); 152 | 153 | this.init(); 154 | } 155 | 156 | init() { 157 | let position = this.$element.css('position'); 158 | 159 | if (this.options.containerSelector) { 160 | this.$container = this.$element.find(this.options.containerSelector); 161 | this.$wrap = this.$element; 162 | 163 | if (position === 'static') { 164 | this.$wrap.css('position', 'relative'); 165 | } 166 | } else { 167 | this.$container = this.$element.wrap('
'); 168 | this.$wrap = this.$container.parent(); 169 | this.$wrap.height(this.$element.height()); 170 | 171 | if (position !== 'static') { 172 | this.$wrap.css('position', position); 173 | } else { 174 | this.$wrap.css('position', 'relative'); 175 | } 176 | } 177 | 178 | if (this.options.contentSelector) { 179 | this.$content = this.$container.find(this.options.contentSelector); 180 | } else { 181 | this.$content = this.$container.wrap('
'); 182 | this.$container = this.$content.parent(); 183 | } 184 | 185 | switch (this.options.direction) { 186 | case 'vertical': { 187 | this.vertical = true; 188 | break; 189 | } 190 | case 'horizontal': { 191 | this.horizontal = true; 192 | break; 193 | } 194 | case 'both': { 195 | this.horizontal = true; 196 | this.vertical = true; 197 | break; 198 | } 199 | case 'auto': { 200 | let overflowX = this.$element.css('overflow-x'), 201 | overflowY = this.$element.css('overflow-y'); 202 | 203 | if (overflowX === 'scroll' || overflowX === 'auto') { 204 | this.horizontal = true; 205 | } 206 | if (overflowY === 'scroll' || overflowY === 'auto') { 207 | this.vertical = true; 208 | } 209 | break; 210 | } 211 | default: { 212 | break; 213 | } 214 | } 215 | 216 | if (!this.vertical && !this.horizontal) { 217 | return; 218 | } 219 | 220 | this.$wrap.addClass(this.classes.wrap); 221 | this.$container.addClass(this.classes.container); 222 | this.$content.addClass(this.classes.content); 223 | 224 | if (this.options.skin) { 225 | this.$wrap.addClass(this.classes.skin); 226 | } 227 | 228 | this.$wrap.addClass(this.options.enabledClass); 229 | 230 | if (this.vertical) { 231 | this.$wrap.addClass(`${this.classes.wrap}-vertical`); 232 | this.initLayout('vertical'); 233 | this.createBar('vertical'); 234 | } 235 | 236 | if (this.horizontal) { 237 | this.$wrap.addClass(`${this.classes.wrap}-horizontal`); 238 | this.initLayout('horizontal'); 239 | this.createBar('horizontal'); 240 | } 241 | 242 | this.bindEvents(); 243 | 244 | this.trigger('ready'); 245 | } 246 | 247 | bindEvents() { 248 | if (this.options.responsive) { 249 | $(window).on(this.eventNameWithId('orientationchange'), () => { 250 | this.update(); 251 | }); 252 | $(window).on(this.eventNameWithId('resize'), this.throttle(() => { 253 | this.update(); 254 | }, this.options.throttle)); 255 | } 256 | 257 | if (!this.horizontal && !this.vertical) { 258 | return; 259 | } 260 | 261 | let that = this; 262 | 263 | this.$wrap.on(this.eventName('mouseenter'), () => { 264 | that.$wrap.addClass(this.options.hoveringClass); 265 | that.enter('hovering'); 266 | that.trigger('hover'); 267 | }); 268 | 269 | this.$wrap.on(this.eventName('mouseleave'), () => { 270 | that.$wrap.removeClass(this.options.hoveringClass); 271 | 272 | if (!that.is('hovering')) { 273 | return; 274 | } 275 | that.leave('hovering'); 276 | that.trigger('hovered'); 277 | }); 278 | 279 | if (this.options.showOnHover) { 280 | if (this.options.showOnBarHover) { 281 | this.$bar.on('asScrollbar::hover', () => { 282 | if(that.horizontal){ 283 | that.showBar('horizontal'); 284 | } 285 | if(that.vertical){ 286 | that.showBar('vertical'); 287 | } 288 | }).on('asScrollbar::hovered', () => { 289 | if(that.horizontal){ 290 | that.hideBar('horizontal'); 291 | } 292 | if(that.vertical){ 293 | that.hideBar('vertical'); 294 | } 295 | }); 296 | } else { 297 | this.$element.on(`${NAMESPACE$1}::hover`, $.proxy(this.showBar, this)); 298 | this.$element.on(`${NAMESPACE$1}::hovered`, $.proxy(this.hideBar, this)); 299 | } 300 | } 301 | 302 | this.$container.on(this.eventName('scroll'), () => { 303 | if (that.horizontal) { 304 | let oldLeft = that.offsetLeft; 305 | that.offsetLeft = that.getOffset('horizontal'); 306 | 307 | if (oldLeft !== that.offsetLeft) { 308 | that.trigger('scroll', that.getPercentOffset('horizontal'), 'horizontal'); 309 | 310 | if (that.offsetLeft === 0) { 311 | that.trigger('scrolltop', 'horizontal'); 312 | } 313 | if (that.offsetLeft === that.getScrollLength('horizontal')) { 314 | that.trigger('scrollend', 'horizontal'); 315 | } 316 | } 317 | } 318 | 319 | if (that.vertical) { 320 | let oldTop = that.offsetTop; 321 | 322 | that.offsetTop = that.getOffset('vertical'); 323 | 324 | if (oldTop !== that.offsetTop) { 325 | that.trigger('scroll', that.getPercentOffset('vertical'), 'vertical'); 326 | 327 | if (that.offsetTop === 0) { 328 | that.trigger('scrolltop', 'vertical'); 329 | } 330 | if (that.offsetTop === that.getScrollLength('vertical')) { 331 | that.trigger('scrollend', 'vertical'); 332 | } 333 | } 334 | } 335 | }); 336 | 337 | this.$element.on(`${NAMESPACE$1}::scroll`, (e, api, value, direction) => { 338 | if (!that.is('scrolling')) { 339 | that.enter('scrolling'); 340 | that.$wrap.addClass(that.options.scrollingClass); 341 | } 342 | let bar = api.getBarApi(direction); 343 | 344 | bar.moveTo(conventToPercentage(value), false, true); 345 | 346 | clearTimeout(that._timeoutId); 347 | that._timeoutId = setTimeout(() => { 348 | that.$wrap.removeClass(that.options.scrollingClass); 349 | that.leave('scrolling'); 350 | }, 200); 351 | }); 352 | 353 | this.$bar.on('asScrollbar::change', (e, api, value) => { 354 | if(typeof e.target.direction === 'string') { 355 | that.scrollTo(e.target.direction, conventToPercentage(value), false, true); 356 | } 357 | }); 358 | 359 | this.$bar.on('asScrollbar::drag', () => { 360 | that.$wrap.addClass(that.options.draggingClass); 361 | }).on('asScrollbar::dragged', () => { 362 | that.$wrap.removeClass(that.options.draggingClass); 363 | }); 364 | } 365 | 366 | unbindEvents() { 367 | this.$wrap.off(this.eventName()); 368 | this.$element.off(`${NAMESPACE$1}::scroll`).off(`${NAMESPACE$1}::hover`).off(`${NAMESPACE$1}::hovered`); 369 | this.$container.off(this.eventName()); 370 | $(window).off(this.eventNameWithId()); 371 | } 372 | 373 | initLayout(direction) { 374 | if (direction === 'vertical') { 375 | this.$container.css('height', this.$wrap.height()); 376 | } 377 | let attributes = this.attributes[direction], 378 | container = this.$container[0]; 379 | 380 | // this.$container.css(attributes.overflow, 'scroll'); 381 | 382 | let parentLength = container.parentNode[attributes.crossClientLength], 383 | scrollbarWidth = this.getBrowserScrollbarWidth(direction); 384 | 385 | this.$content.css(attributes.crossLength, `${parentLength}px`); 386 | this.$container.css(attributes.crossLength, `${scrollbarWidth + parentLength}px`); 387 | 388 | if (scrollbarWidth === 0 && isFFLionScrollbar) { 389 | this.$container.css(attributes.ffPadding, 16); 390 | } 391 | } 392 | 393 | createBar(direction) { 394 | let options = $.extend(this.options.scrollbar, { 395 | namespace: this.classes.bar, 396 | direction: direction, 397 | useCssTransitions: false, 398 | keyboard: false 399 | }); 400 | let $bar = $('
'); 401 | $bar.asScrollbar(options); 402 | 403 | if (this.options.showOnHover) { 404 | $bar.addClass(this.classes.barHide); 405 | } 406 | 407 | $bar.appendTo(this.$wrap); 408 | 409 | this[`$${direction}`] = $bar; 410 | 411 | if (this.$bar === null) { 412 | this.$bar = $bar; 413 | } else { 414 | this.$bar = this.$bar.add($bar); 415 | } 416 | 417 | this.updateBarHandle(direction); 418 | } 419 | 420 | trigger(eventType, ...params) { 421 | const data = [this].concat(params); 422 | 423 | // event 424 | this.$element.trigger(`${NAMESPACE$1}::${eventType}`, data); 425 | 426 | // callback 427 | eventType = eventType.replace(/\b\w+\b/g, (word) => { 428 | return word.substring(0, 1).toUpperCase() + word.substring(1); 429 | }); 430 | const onFunction = `on${eventType}`; 431 | 432 | if (typeof this.options[onFunction] === 'function') { 433 | this.options[onFunction].apply(this, params); 434 | } 435 | } 436 | 437 | /** 438 | * Checks whether the carousel is in a specific state or not. 439 | */ 440 | is(state) { 441 | return this._states[state] && this._states[state] > 0; 442 | } 443 | 444 | /** 445 | * Enters a state. 446 | */ 447 | enter(state) { 448 | if (this._states[state] === undefined) { 449 | this._states[state] = 0; 450 | } 451 | 452 | // this._states[state]++; 453 | this._states[state] = 1; 454 | } 455 | 456 | /** 457 | * Leaves a state. 458 | */ 459 | leave(state) { 460 | // this._states[state]--; 461 | 462 | this._states[state] = -1; 463 | } 464 | 465 | eventName(events) { 466 | if (typeof events !== 'string' || events === '') { 467 | return `.${this.options.namespace}`; 468 | } 469 | 470 | events = events.split(' '); 471 | let length = events.length; 472 | for (let i = 0; i < length; i++) { 473 | events[i] = `${events[i]}.${this.options.namespace}`; 474 | } 475 | return events.join(' '); 476 | } 477 | 478 | eventNameWithId(events) { 479 | if (typeof events !== 'string' || events === '') { 480 | return `.${this.options.namespace}-${this.instanceId}`; 481 | } 482 | 483 | events = events.split(' '); 484 | let length = events.length; 485 | for (let i = 0; i < length; i++) { 486 | events[i] = `${events[i]}.${this.options.namespace}-${this.instanceId}`; 487 | } 488 | return events.join(' '); 489 | } 490 | 491 | /** 492 | * _throttle 493 | * @description Borrowed from Underscore.js 494 | */ 495 | throttle(func, wait) { 496 | const _now = Date.now || function() { 497 | return new Date().getTime(); 498 | }; 499 | 500 | let timeout; 501 | let context; 502 | let args; 503 | let result; 504 | let previous = 0; 505 | let later = function() { 506 | previous = _now(); 507 | timeout = null; 508 | result = func.apply(context, args); 509 | if (!timeout) { 510 | context = args = null; 511 | } 512 | }; 513 | 514 | return (...params) => { 515 | /*eslint consistent-this: "off"*/ 516 | let now = _now(); 517 | let remaining = wait - (now - previous); 518 | context = this; 519 | args = params; 520 | if (remaining <= 0 || remaining > wait) { 521 | if (timeout) { 522 | clearTimeout(timeout); 523 | timeout = null; 524 | } 525 | previous = now; 526 | result = func.apply(context, args); 527 | if (!timeout) { 528 | context = args = null; 529 | } 530 | } else if (!timeout) { 531 | timeout = setTimeout(later, remaining); 532 | } 533 | return result; 534 | }; 535 | } 536 | 537 | getBrowserScrollbarWidth(direction) { 538 | let attributes = this.attributes[direction], 539 | outer, outerStyle; 540 | if (attributes.scrollbarWidth) { 541 | return attributes.scrollbarWidth; 542 | } 543 | outer = document.createElement('div'); 544 | outerStyle = outer.style; 545 | outerStyle.position = 'absolute'; 546 | outerStyle.width = '100px'; 547 | outerStyle.height = '100px'; 548 | outerStyle.overflow = 'scroll'; 549 | outerStyle.top = '-9999px'; 550 | document.body.appendChild(outer); 551 | attributes.scrollbarWidth = outer[attributes.offset] - outer[attributes.clientLength]; 552 | document.body.removeChild(outer); 553 | return attributes.scrollbarWidth; 554 | } 555 | 556 | getOffset(direction) { 557 | let attributes = this.attributes[direction], 558 | container = this.$container[0]; 559 | 560 | return (container[attributes.pageOffset] || container[attributes.scroll]); 561 | } 562 | 563 | getPercentOffset(direction) { 564 | return this.getOffset(direction) / this.getScrollLength(direction); 565 | } 566 | 567 | getContainerLength(direction) { 568 | return this.$container[0][this.attributes[direction].clientLength]; 569 | } 570 | 571 | getScrollLength(direction) { 572 | let scrollLength = this.$content[0][this.attributes[direction].scrollLength]; 573 | return scrollLength - this.getContainerLength(direction); 574 | } 575 | 576 | scrollTo(direction, value, trigger, sync) { 577 | let type = typeof value; 578 | 579 | if (type === 'string') { 580 | if (isPercentage(value)) { 581 | value = convertPercentageToFloat(value) * this.getScrollLength(direction); 582 | } 583 | 584 | value = parseFloat(value); 585 | type = 'number'; 586 | } 587 | 588 | if (type !== 'number') { 589 | return; 590 | } 591 | 592 | this.move(direction, value, trigger, sync); 593 | } 594 | 595 | scrollBy(direction, value, trigger, sync) { 596 | let type = typeof value; 597 | 598 | if (type === 'string') { 599 | if (isPercentage(value)) { 600 | value = convertPercentageToFloat(value) * this.getScrollLength(direction); 601 | } 602 | 603 | value = parseFloat(value); 604 | type = 'number'; 605 | } 606 | 607 | if (type !== 'number') { 608 | return; 609 | } 610 | 611 | this.move(direction, this.getOffset(direction) + value, trigger, sync); 612 | } 613 | 614 | move(direction, value, trigger, sync) { 615 | if (this[direction] !== true || typeof value !== 'number') { 616 | return; 617 | } 618 | 619 | this.enter('moving'); 620 | 621 | if (value < 0) { 622 | value = 0; 623 | } else if (value > this.getScrollLength(direction)) { 624 | value = this.getScrollLength(direction); 625 | } 626 | 627 | let attributes = this.attributes[direction]; 628 | 629 | var that = this; 630 | let callback = () => { 631 | that.leave('moving'); 632 | }; 633 | 634 | if (sync) { 635 | this.$container[0][attributes.scroll] = value; 636 | 637 | if (trigger !== false) { 638 | this.trigger('change', value / this.getScrollLength(direction), direction); 639 | } 640 | callback(); 641 | } else { 642 | this.enter('animating'); 643 | 644 | let startTime = getTime(); 645 | let start = this.getOffset(direction); 646 | let end = value; 647 | 648 | let run = (time) => { 649 | let percent = (time - startTime) / that.options.duration; 650 | 651 | if (percent > 1) { 652 | percent = 1; 653 | } 654 | 655 | percent = that.easing.fn(percent); 656 | 657 | let current = parseFloat(start + percent * (end - start), 10); 658 | that.$container[0][attributes.scroll] = current; 659 | 660 | if (trigger !== false) { 661 | that.trigger('change', value / that.getScrollLength(direction), direction); 662 | } 663 | 664 | if (percent === 1) { 665 | window.cancelAnimationFrame(that._frameId); 666 | that._frameId = null; 667 | 668 | that.leave('animating'); 669 | callback(); 670 | } else { 671 | that._frameId = window.requestAnimationFrame(run); 672 | } 673 | }; 674 | 675 | this._frameId = window.requestAnimationFrame(run); 676 | } 677 | } 678 | 679 | scrollXto(value, trigger, sync) { 680 | return this.scrollTo('horizontal', value, trigger, sync); 681 | } 682 | 683 | scrollYto(value, trigger, sync) { 684 | return this.scrollTo('vertical', value, trigger, sync); 685 | } 686 | 687 | scrollXby(value, trigger, sync) { 688 | return this.scrollBy('horizontal', value, trigger, sync); 689 | } 690 | 691 | scrollYby(value, trigger, sync) { 692 | return this.scrollBy('vertical', value, trigger, sync); 693 | } 694 | 695 | getBar(direction) { 696 | if (direction && this[`$${direction}`]) { 697 | return this[`$${direction}`]; 698 | } 699 | return this.$bar; 700 | } 701 | 702 | getBarApi(direction) { 703 | return this.getBar(direction).data('asScrollbar'); 704 | } 705 | 706 | getBarX() { 707 | return this.getBar('horizontal'); 708 | } 709 | 710 | getBarY() { 711 | return this.getBar('vertical'); 712 | } 713 | 714 | showBar(direction) { 715 | this.getBar(direction).removeClass(this.classes.barHide); 716 | } 717 | 718 | hideBar(direction) { 719 | this.getBar(direction).addClass(this.classes.barHide); 720 | } 721 | 722 | updateBarHandle(direction) { 723 | let api = this.getBarApi(direction); 724 | 725 | if (!api) { 726 | return; 727 | } 728 | 729 | let containerLength = this.getContainerLength(direction), 730 | scrollLength = this.getScrollLength(direction); 731 | 732 | if (scrollLength > 0) { 733 | if (api.is('disabled')) { 734 | api.enable(); 735 | } 736 | api.setHandleLength(api.getBarLength() * containerLength / (scrollLength + containerLength), true); 737 | } else { 738 | 739 | api.disable(); 740 | } 741 | } 742 | 743 | disable() { 744 | if (!this.is('disabled')) { 745 | this.enter('disabled'); 746 | this.$wrap.addClass(this.options.disabledClass).removeClass(this.options.enabledClass); 747 | 748 | this.unbindEvents(); 749 | this.unStyle(); 750 | } 751 | 752 | this.trigger('disable'); 753 | } 754 | 755 | enable() { 756 | if (this.is('disabled')) { 757 | this.leave('disabled'); 758 | this.$wrap.addClass(this.options.enabledClass).removeClass(this.options.disabledClass); 759 | 760 | this.bindEvents(); 761 | this.update(); 762 | } 763 | 764 | this.trigger('enable'); 765 | } 766 | 767 | update() { 768 | if (this.is('disabled')) { 769 | return; 770 | } 771 | if(this.$element.is(':visible')) { 772 | if (this.vertical) { 773 | this.initLayout('vertical'); 774 | this.updateBarHandle('vertical'); 775 | } 776 | if (this.horizontal) { 777 | this.initLayout('horizontal'); 778 | this.updateBarHandle('horizontal'); 779 | } 780 | } 781 | } 782 | 783 | unStyle() { 784 | if (this.horizontal) { 785 | this.$container.css({ 786 | height: '', 787 | 'padding-bottom': '' 788 | }); 789 | this.$content.css({ 790 | height: '' 791 | }); 792 | } 793 | if (this.vertical) { 794 | this.$container.css({ 795 | width: '', 796 | height: '', 797 | 'padding-right': '' 798 | }); 799 | this.$content.css({ 800 | width: '' 801 | }); 802 | } 803 | if (!this.options.containerSelector) { 804 | this.$wrap.css({ 805 | height: '' 806 | }); 807 | } 808 | } 809 | 810 | destroy() { 811 | this.$wrap.removeClass(`${this.classes.wrap}-vertical`) 812 | .removeClass(`${this.classes.wrap}-horizontal`) 813 | .removeClass(this.classes.wrap) 814 | .removeClass(this.options.enabledClass) 815 | .removeClass(this.classes.disabledClass); 816 | this.unStyle(); 817 | 818 | if (this.$bar) { 819 | this.$bar.remove(); 820 | } 821 | 822 | this.unbindEvents(); 823 | 824 | if (this.options.containerSelector) { 825 | this.$container.removeClass(this.classes.container); 826 | } else { 827 | this.$container.unwrap(); 828 | } 829 | if (!this.options.contentSelector) { 830 | this.$content.unwrap(); 831 | } 832 | this.$content.removeClass(this.classes.content); 833 | this.$element.data(NAMESPACE$1, null); 834 | this.trigger('destroy'); 835 | } 836 | 837 | static setDefaults(options) { 838 | $.extend(DEFAULTS, $.isPlainObject(options) && options); 839 | } 840 | } 841 | 842 | var info = { 843 | version:'0.4.10' 844 | }; 845 | 846 | const NAMESPACE = 'asScrollable'; 847 | const OtherAsScrollable = $.fn.asScrollable; 848 | 849 | const jQueryAsScrollable = function(options, ...args) { 850 | if (typeof options === 'string') { 851 | let method = options; 852 | 853 | if (/^_/.test(method)) { 854 | return false; 855 | } else if ((/^(get)/.test(method))) { 856 | let instance = this.first().data(NAMESPACE); 857 | if (instance && typeof instance[method] === 'function') { 858 | return instance[method](...args); 859 | } 860 | } else { 861 | return this.each(function() { 862 | let instance = $.data(this, NAMESPACE); 863 | if (instance && typeof instance[method] === 'function') { 864 | instance[method](...args); 865 | } 866 | }); 867 | } 868 | } 869 | 870 | return this.each(function() { 871 | if (!$(this).data(NAMESPACE)) { 872 | $(this).data(NAMESPACE, new AsScrollable(this, options)); 873 | } 874 | }); 875 | }; 876 | 877 | $.fn.asScrollable = jQueryAsScrollable; 878 | 879 | $.asScrollable = $.extend({ 880 | setDefaults: AsScrollable.setDefaults, 881 | noConflict: function() { 882 | $.fn.asScrollable = OtherAsScrollable; 883 | return jQueryAsScrollable; 884 | } 885 | }, info); 886 | -------------------------------------------------------------------------------- /dist/jquery-asScrollable.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery asScrollable v0.4.10 3 | * https://github.com/amazingSurge/jquery-asScrollable 4 | * 5 | * Copyright (c) amazingSurge 6 | * Released under the LGPL-3.0 license 7 | */ 8 | !function(t,e){if("function"==typeof define&&define.amd)define(["jquery"],e);else if("undefined"!=typeof exports)e(require("jquery"));else{var i={exports:{}};e(t.jQuery),t.jqueryAsScrollableEs=i.exports}}(this,function(t){"use strict";function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(){return void 0!==window.performance&&window.performance.now?window.performance.now():Date.now()}function s(t){return"string"==typeof t&&-1!==t.indexOf("%")}function n(t){return t<0?t=0:t>1&&(t=1),100*parseFloat(t).toFixed(4)+"%"}function a(t){return parseFloat(t.slice(0,-1)/100,10)}var o=function(t){return t&&t.__esModule?t:{default:t}}(t),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},l=function(){function t(t,e){for(var i=0;i23)}(),f=0,u=function(){function t(i,s){e(this,t),this.$element=(0,o.default)(i),s=this.options=o.default.extend({},h,s||{},this.$element.data("options")||{}),this.classes={wrap:s.namespace,content:s.namespace+"-content",container:s.namespace+"-container",bar:s.namespace+"-bar",barHide:s.namespace+"-bar-hide",skin:s.skin},this.attributes={vertical:{axis:"Y",overflow:"overflow-y",scroll:"scrollTop",scrollLength:"scrollHeight",pageOffset:"pageYOffset",ffPadding:"padding-right",length:"height",clientLength:"clientHeight",offset:"offsetHeight",crossLength:"width",crossClientLength:"clientWidth",crossOffset:"offsetWidth"},horizontal:{axis:"X",overflow:"overflow-x",scroll:"scrollLeft",scrollLength:"scrollWidth",pageOffset:"pageXOffset",ffPadding:"padding-bottom",length:"width",clientLength:"clientWidth",offset:"offsetWidth",crossLength:"height",crossClientLength:"clientHeight",crossOffset:"offsetHeight"}},this._states={},this.horizontal=null,this.vertical=null,this.$bar=null,this._frameId=null,this._timeoutId=null,this.instanceId=++f,this.easing=o.default.asScrollbar.getEasing(this.options.easing)||o.default.asScrollbar.getEasing("ease"),this.init()}return l(t,[{key:"init",value:function(){var t=this.$element.css("position");switch(this.options.containerSelector?(this.$container=this.$element.find(this.options.containerSelector),this.$wrap=this.$element,"static"===t&&this.$wrap.css("position","relative")):(this.$container=this.$element.wrap("
"),this.$wrap=this.$container.parent(),this.$wrap.height(this.$element.height()),"static"!==t?this.$wrap.css("position",t):this.$wrap.css("position","relative")),this.options.contentSelector?this.$content=this.$container.find(this.options.contentSelector):(this.$content=this.$container.wrap("
"),this.$container=this.$content.parent()),this.options.direction){case"vertical":this.vertical=!0;break;case"horizontal":this.horizontal=!0;break;case"both":this.horizontal=!0,this.vertical=!0;break;case"auto":var e=this.$element.css("overflow-x"),i=this.$element.css("overflow-y");"scroll"!==e&&"auto"!==e||(this.horizontal=!0),"scroll"!==i&&"auto"!==i||(this.vertical=!0)}(this.vertical||this.horizontal)&&(this.$wrap.addClass(this.classes.wrap),this.$container.addClass(this.classes.container),this.$content.addClass(this.classes.content),this.options.skin&&this.$wrap.addClass(this.classes.skin),this.$wrap.addClass(this.options.enabledClass),this.vertical&&(this.$wrap.addClass(this.classes.wrap+"-vertical"),this.initLayout("vertical"),this.createBar("vertical")),this.horizontal&&(this.$wrap.addClass(this.classes.wrap+"-horizontal"),this.initLayout("horizontal"),this.createBar("horizontal")),this.bindEvents(),this.trigger("ready"))}},{key:"bindEvents",value:function(){var t=this;if(this.options.responsive&&((0,o.default)(window).on(this.eventNameWithId("orientationchange"),function(){t.update()}),(0,o.default)(window).on(this.eventNameWithId("resize"),this.throttle(function(){t.update()},this.options.throttle))),this.horizontal||this.vertical){var e=this;this.$wrap.on(this.eventName("mouseenter"),function(){e.$wrap.addClass(t.options.hoveringClass),e.enter("hovering"),e.trigger("hover")}),this.$wrap.on(this.eventName("mouseleave"),function(){e.$wrap.removeClass(t.options.hoveringClass),e.is("hovering")&&(e.leave("hovering"),e.trigger("hovered"))}),this.options.showOnHover&&(this.options.showOnBarHover?this.$bar.on("asScrollbar::hover",function(){e.horizontal&&e.showBar("horizontal"),e.vertical&&e.showBar("vertical")}).on("asScrollbar::hovered",function(){e.horizontal&&e.hideBar("horizontal"),e.vertical&&e.hideBar("vertical")}):(this.$element.on("asScrollable::hover",o.default.proxy(this.showBar,this)),this.$element.on("asScrollable::hovered",o.default.proxy(this.hideBar,this)))),this.$container.on(this.eventName("scroll"),function(){if(e.horizontal){var t=e.offsetLeft;e.offsetLeft=e.getOffset("horizontal"),t!==e.offsetLeft&&(e.trigger("scroll",e.getPercentOffset("horizontal"),"horizontal"),0===e.offsetLeft&&e.trigger("scrolltop","horizontal"),e.offsetLeft===e.getScrollLength("horizontal")&&e.trigger("scrollend","horizontal"))}if(e.vertical){var i=e.offsetTop;e.offsetTop=e.getOffset("vertical"),i!==e.offsetTop&&(e.trigger("scroll",e.getPercentOffset("vertical"),"vertical"),0===e.offsetTop&&e.trigger("scrolltop","vertical"),e.offsetTop===e.getScrollLength("vertical")&&e.trigger("scrollend","vertical"))}}),this.$element.on("asScrollable::scroll",function(t,i,s,a){e.is("scrolling")||(e.enter("scrolling"),e.$wrap.addClass(e.options.scrollingClass)),i.getBarApi(a).moveTo(n(s),!1,!0),clearTimeout(e._timeoutId),e._timeoutId=setTimeout(function(){e.$wrap.removeClass(e.options.scrollingClass),e.leave("scrolling")},200)}),this.$bar.on("asScrollbar::change",function(t,i,s){"string"==typeof t.target.direction&&e.scrollTo(t.target.direction,n(s),!1,!0)}),this.$bar.on("asScrollbar::drag",function(){e.$wrap.addClass(e.options.draggingClass)}).on("asScrollbar::dragged",function(){e.$wrap.removeClass(e.options.draggingClass)})}}},{key:"unbindEvents",value:function(){this.$wrap.off(this.eventName()),this.$element.off("asScrollable::scroll").off("asScrollable::hover").off("asScrollable::hovered"),this.$container.off(this.eventName()),(0,o.default)(window).off(this.eventNameWithId())}},{key:"initLayout",value:function(t){"vertical"===t&&this.$container.css("height",this.$wrap.height());var e=this.attributes[t],i=this.$container[0].parentNode[e.crossClientLength],s=this.getBrowserScrollbarWidth(t);this.$content.css(e.crossLength,i+"px"),this.$container.css(e.crossLength,s+i+"px"),0===s&&c&&this.$container.css(e.ffPadding,16)}},{key:"createBar",value:function(t){var e=o.default.extend(this.options.scrollbar,{namespace:this.classes.bar,direction:t,useCssTransitions:!1,keyboard:!1}),i=(0,o.default)("
");i.asScrollbar(e),this.options.showOnHover&&i.addClass(this.classes.barHide),i.appendTo(this.$wrap),this["$"+t]=i,null===this.$bar?this.$bar=i:this.$bar=this.$bar.add(i),this.updateBarHandle(t)}},{key:"trigger",value:function(t){for(var e=arguments.length,i=Array(e>1?e-1:0),s=1;s0}},{key:"enter",value:function(t){void 0===this._states[t]&&(this._states[t]=0),this._states[t]=1}},{key:"leave",value:function(t){this._states[t]=-1}},{key:"eventName",value:function(t){if("string"!=typeof t||""===t)return"."+this.options.namespace;for(var e=(t=t.split(" ")).length,i=0;ie?(n&&(clearTimeout(n),n=null),l=d,r=t.apply(a,o),n||(a=o=null)):n||(n=setTimeout(h,v)),r}}},{key:"getBrowserScrollbarWidth",value:function(t){var e=this.attributes[t],i=void 0,s=void 0;return e.scrollbarWidth?e.scrollbarWidth:(i=document.createElement("div"),s=i.style,s.position="absolute",s.width="100px",s.height="100px",s.overflow="scroll",s.top="-9999px",document.body.appendChild(i),e.scrollbarWidth=i[e.offset]-i[e.clientLength],document.body.removeChild(i),e.scrollbarWidth)}},{key:"getOffset",value:function(t){var e=this.attributes[t],i=this.$container[0];return i[e.pageOffset]||i[e.scroll]}},{key:"getPercentOffset",value:function(t){return this.getOffset(t)/this.getScrollLength(t)}},{key:"getContainerLength",value:function(t){return this.$container[0][this.attributes[t].clientLength]}},{key:"getScrollLength",value:function(t){return this.$content[0][this.attributes[t].scrollLength]-this.getContainerLength(t)}},{key:"scrollTo",value:function(t,e,i,n){var o=void 0===e?"undefined":r(e);"string"===o&&(s(e)&&(e=a(e)*this.getScrollLength(t)),e=parseFloat(e),o="number"),"number"===o&&this.move(t,e,i,n)}},{key:"scrollBy",value:function(t,e,i,n){var o=void 0===e?"undefined":r(e);"string"===o&&(s(e)&&(e=a(e)*this.getScrollLength(t)),e=parseFloat(e),o="number"),"number"===o&&this.move(t,this.getOffset(t)+e,i,n)}},{key:"move",value:function(t,e,s,n){if(!0===this[t]&&"number"==typeof e){this.enter("moving"),e<0?e=0:e>this.getScrollLength(t)&&(e=this.getScrollLength(t));var a=this.attributes[t],o=this,r=function(){o.leave("moving")};if(n)this.$container[0][a.scroll]=e,!1!==s&&this.trigger("change",e/this.getScrollLength(t),t),r();else{this.enter("animating");var l=i(),h=this.getOffset(t),c=e;this._frameId=window.requestAnimationFrame(function i(n){var f=(n-l)/o.options.duration;f>1&&(f=1),f=o.easing.fn(f);var u=parseFloat(h+f*(c-h),10);o.$container[0][a.scroll]=u,!1!==s&&o.trigger("change",e/o.getScrollLength(t),t),1===f?(window.cancelAnimationFrame(o._frameId),o._frameId=null,o.leave("animating"),r()):o._frameId=window.requestAnimationFrame(i)})}}}},{key:"scrollXto",value:function(t,e,i){return this.scrollTo("horizontal",t,e,i)}},{key:"scrollYto",value:function(t,e,i){return this.scrollTo("vertical",t,e,i)}},{key:"scrollXby",value:function(t,e,i){return this.scrollBy("horizontal",t,e,i)}},{key:"scrollYby",value:function(t,e,i){return this.scrollBy("vertical",t,e,i)}},{key:"getBar",value:function(t){return t&&this["$"+t]?this["$"+t]:this.$bar}},{key:"getBarApi",value:function(t){return this.getBar(t).data("asScrollbar")}},{key:"getBarX",value:function(){return this.getBar("horizontal")}},{key:"getBarY",value:function(){return this.getBar("vertical")}},{key:"showBar",value:function(t){this.getBar(t).removeClass(this.classes.barHide)}},{key:"hideBar",value:function(t){this.getBar(t).addClass(this.classes.barHide)}},{key:"updateBarHandle",value:function(t){var e=this.getBarApi(t);if(e){var i=this.getContainerLength(t),s=this.getScrollLength(t);s>0?(e.is("disabled")&&e.enable(),e.setHandleLength(e.getBarLength()*i/(s+i),!0)):e.disable()}}},{key:"disable",value:function(){this.is("disabled")||(this.enter("disabled"),this.$wrap.addClass(this.options.disabledClass).removeClass(this.options.enabledClass),this.unbindEvents(),this.unStyle()),this.trigger("disable")}},{key:"enable",value:function(){this.is("disabled")&&(this.leave("disabled"),this.$wrap.addClass(this.options.enabledClass).removeClass(this.options.disabledClass),this.bindEvents(),this.update()),this.trigger("enable")}},{key:"update",value:function(){this.is("disabled")||this.$element.is(":visible")&&(this.vertical&&(this.initLayout("vertical"),this.updateBarHandle("vertical")),this.horizontal&&(this.initLayout("horizontal"),this.updateBarHandle("horizontal")))}},{key:"unStyle",value:function(){this.horizontal&&(this.$container.css({height:"","padding-bottom":""}),this.$content.css({height:""})),this.vertical&&(this.$container.css({width:"",height:"","padding-right":""}),this.$content.css({width:""})),this.options.containerSelector||this.$wrap.css({height:""})}},{key:"destroy",value:function(){this.$wrap.removeClass(this.classes.wrap+"-vertical").removeClass(this.classes.wrap+"-horizontal").removeClass(this.classes.wrap).removeClass(this.options.enabledClass).removeClass(this.classes.disabledClass),this.unStyle(),this.$bar&&this.$bar.remove(),this.unbindEvents(),this.options.containerSelector?this.$container.removeClass(this.classes.container):this.$container.unwrap(),this.options.contentSelector||this.$content.unwrap(),this.$content.removeClass(this.classes.content),this.$element.data("asScrollable",null),this.trigger("destroy")}}],[{key:"setDefaults",value:function(t){o.default.extend(h,o.default.isPlainObject(t)&&t)}}]),t}(),d={version:"0.4.10"},v=o.default.fn.asScrollable,g=function(t){for(var e=arguments.length,i=Array(e>1?e-1:0),s=1;s 2 | 3 | 4 | 5 | 6 | jQuery asScrollable 7 | 8 | 9 | 10 | 11 | 12 | 39 | 40 | 41 | 44 |
45 |
46 |
47 |

absolute Scrollable

48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 |
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 |
73 | 74 | 75 |
76 |
77 |
78 |
79 | 80 | 81 | 82 | 83 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /examples/css/main.css: -------------------------------------------------------------------------------- 1 | /*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */ 2 | 3 | /* 4 | * What follows is the result of much research on cross-browser styling. 5 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 6 | * Kroc Camen, and the H5BP dev community and team. 7 | */ 8 | 9 | /* ========================================================================== 10 | Base styles: opinionated defaults 11 | ========================================================================== */ 12 | 13 | html { 14 | color: #222; 15 | font-size: 1em; 16 | line-height: 1.4; 17 | } 18 | 19 | /* 20 | * Remove text-shadow in selection highlight: 21 | * https://twitter.com/miketaylr/status/12228805301 22 | * 23 | * These selection rule sets have to be separate. 24 | * Customize the background color to match your design. 25 | */ 26 | 27 | ::-moz-selection { 28 | background: #b3d4fc; 29 | text-shadow: none; 30 | } 31 | 32 | ::selection { 33 | background: #b3d4fc; 34 | text-shadow: none; 35 | } 36 | 37 | /* 38 | * A better looking default horizontal rule 39 | */ 40 | 41 | hr { 42 | display: block; 43 | height: 1px; 44 | border: 0; 45 | border-top: 1px solid #ccc; 46 | margin: 1em 0; 47 | padding: 0; 48 | } 49 | 50 | /* 51 | * Remove the gap between audio, canvas, iframes, 52 | * images, videos and the bottom of their containers: 53 | * https://github.com/h5bp/html5-boilerplate/issues/440 54 | */ 55 | 56 | audio, 57 | canvas, 58 | iframe, 59 | img, 60 | svg, 61 | video { 62 | vertical-align: middle; 63 | } 64 | 65 | /* 66 | * Remove default fieldset styles. 67 | */ 68 | 69 | fieldset { 70 | border: 0; 71 | margin: 0; 72 | padding: 0; 73 | } 74 | 75 | /* 76 | * Allow only vertical resizing of textareas. 77 | */ 78 | 79 | textarea { 80 | resize: vertical; 81 | } 82 | 83 | /* ========================================================================== 84 | Browser Upgrade Prompt 85 | ========================================================================== */ 86 | 87 | .browserupgrade { 88 | margin: 0.2em 0; 89 | background: #ccc; 90 | color: #000; 91 | padding: 0.2em 0; 92 | } 93 | 94 | /* ========================================================================== 95 | Author's custom styles 96 | ========================================================================== */ 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | /* ========================================================================== 115 | Helper classes 116 | ========================================================================== */ 117 | 118 | /* 119 | * Hide visually and from screen readers: 120 | * http://juicystudio.com/article/screen-readers-display-none.php 121 | */ 122 | 123 | .hidden { 124 | display: none !important; 125 | visibility: hidden; 126 | } 127 | 128 | /* 129 | * Hide only visually, but have it available for screen readers: 130 | * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility 131 | */ 132 | 133 | .visuallyhidden { 134 | border: 0; 135 | clip: rect(0 0 0 0); 136 | height: 1px; 137 | margin: -1px; 138 | overflow: hidden; 139 | padding: 0; 140 | position: absolute; 141 | width: 1px; 142 | } 143 | 144 | /* 145 | * Extends the .visuallyhidden class to allow the element 146 | * to be focusable when navigated to via the keyboard: 147 | * https://www.drupal.org/node/897638 148 | */ 149 | 150 | .visuallyhidden.focusable:active, 151 | .visuallyhidden.focusable:focus { 152 | clip: auto; 153 | height: auto; 154 | margin: 0; 155 | overflow: visible; 156 | position: static; 157 | width: auto; 158 | } 159 | 160 | /* 161 | * Hide visually and from screen readers, but maintain layout 162 | */ 163 | 164 | .invisible { 165 | visibility: hidden; 166 | } 167 | 168 | /* 169 | * Clearfix: contain floats 170 | * 171 | * For modern browsers 172 | * 1. The space content is one way to avoid an Opera bug when the 173 | * `contenteditable` attribute is included anywhere else in the document. 174 | * Otherwise it causes space to appear at the top and bottom of elements 175 | * that receive the `clearfix` class. 176 | * 2. The use of `table` rather than `block` is only necessary if using 177 | * `:before` to contain the top-margins of child elements. 178 | */ 179 | 180 | .clearfix:before, 181 | .clearfix:after { 182 | content: " "; /* 1 */ 183 | display: table; /* 2 */ 184 | } 185 | 186 | .clearfix:after { 187 | clear: both; 188 | } 189 | 190 | /* ========================================================================== 191 | EXAMPLE Media Queries for Responsive Design. 192 | These examples override the primary ('mobile first') styles. 193 | Modify as content requires. 194 | ========================================================================== */ 195 | 196 | @media only screen and (min-width: 35em) { 197 | /* Style adjustments for viewports that meet the condition */ 198 | } 199 | 200 | @media print, 201 | (-o-min-device-pixel-ratio: 5/4), 202 | (-webkit-min-device-pixel-ratio: 1.25), 203 | (min-resolution: 120dpi) { 204 | /* Style adjustments for high resolution devices */ 205 | } 206 | 207 | /* ========================================================================== 208 | Print styles. 209 | Inlined to avoid the additional HTTP request: 210 | http://www.phpied.com/delay-loading-your-print-css/ 211 | ========================================================================== */ 212 | 213 | @media print { 214 | *, 215 | *:before, 216 | *:after { 217 | background: transparent !important; 218 | color: #000 !important; /* Black prints faster: 219 | http://www.sanbeiji.com/archives/953 */ 220 | box-shadow: none !important; 221 | text-shadow: none !important; 222 | } 223 | 224 | a, 225 | a:visited { 226 | text-decoration: underline; 227 | } 228 | 229 | a[href]:after { 230 | content: " (" attr(href) ")"; 231 | } 232 | 233 | abbr[title]:after { 234 | content: " (" attr(title) ")"; 235 | } 236 | 237 | /* 238 | * Don't show links that are fragment identifiers, 239 | * or use the `javascript:` pseudo protocol 240 | */ 241 | 242 | a[href^="#"]:after, 243 | a[href^="javascript:"]:after { 244 | content: ""; 245 | } 246 | 247 | pre, 248 | blockquote { 249 | border: 1px solid #999; 250 | page-break-inside: avoid; 251 | } 252 | 253 | /* 254 | * Printing Tables: 255 | * http://css-discuss.incutio.com/wiki/Printing_Tables 256 | */ 257 | 258 | thead { 259 | display: table-header-group; 260 | } 261 | 262 | tr, 263 | img { 264 | page-break-inside: avoid; 265 | } 266 | 267 | img { 268 | max-width: 100% !important; 269 | } 270 | 271 | p, 272 | h2, 273 | h3 { 274 | orphans: 3; 275 | widows: 3; 276 | } 277 | 278 | h2, 279 | h3 { 280 | page-break-after: avoid; 281 | } 282 | } -------------------------------------------------------------------------------- /examples/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Correct the line height in all browsers. 6 | * 3. Prevent adjustments of font size after orientation changes in IE and iOS. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | line-height: 1.15; /* 2 */ 12 | -ms-text-size-adjust: 100%; /* 3 */ 13 | -webkit-text-size-adjust: 100%; /* 3 */ 14 | } 15 | 16 | /** 17 | * Remove the margin in all browsers (opinionated). 18 | */ 19 | 20 | body { 21 | margin: 0; 22 | } 23 | 24 | /* HTML5 display definitions 25 | ========================================================================== */ 26 | 27 | /** 28 | * Add the correct display in IE 9-. 29 | * 1. Add the correct display in Edge, IE, and Firefox. 30 | * 2. Add the correct display in IE. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, /* 1 */ 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | main, /* 2 */ 41 | menu, 42 | nav, 43 | section, 44 | summary { /* 1 */ 45 | display: block; 46 | } 47 | 48 | /** 49 | * Add the correct display in IE 9-. 50 | */ 51 | 52 | audio, 53 | canvas, 54 | progress, 55 | video { 56 | display: inline-block; 57 | } 58 | 59 | /** 60 | * Add the correct display in iOS 4-7. 61 | */ 62 | 63 | audio:not([controls]) { 64 | display: none; 65 | height: 0; 66 | } 67 | 68 | /** 69 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 70 | */ 71 | 72 | progress { 73 | vertical-align: baseline; 74 | } 75 | 76 | /** 77 | * Add the correct display in IE 10-. 78 | * 1. Add the correct display in IE. 79 | */ 80 | 81 | template, /* 1 */ 82 | [hidden] { 83 | display: none; 84 | } 85 | 86 | /* Links 87 | ========================================================================== */ 88 | 89 | /** 90 | * 1. Remove the gray background on active links in IE 10. 91 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 92 | */ 93 | 94 | a { 95 | background-color: transparent; /* 1 */ 96 | -webkit-text-decoration-skip: objects; /* 2 */ 97 | } 98 | 99 | /** 100 | * Remove the outline on focused links when they are also active or hovered 101 | * in all browsers (opinionated). 102 | */ 103 | 104 | a:active, 105 | a:hover { 106 | outline-width: 0; 107 | } 108 | 109 | /* Text-level semantics 110 | ========================================================================== */ 111 | 112 | /** 113 | * 1. Remove the bottom border in Firefox 39-. 114 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 115 | */ 116 | 117 | abbr[title] { 118 | border-bottom: none; /* 1 */ 119 | text-decoration: underline; /* 2 */ 120 | text-decoration: underline dotted; /* 2 */ 121 | } 122 | 123 | /** 124 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 125 | */ 126 | 127 | b, 128 | strong { 129 | font-weight: inherit; 130 | } 131 | 132 | /** 133 | * Add the correct font weight in Chrome, Edge, and Safari. 134 | */ 135 | 136 | b, 137 | strong { 138 | font-weight: bolder; 139 | } 140 | 141 | /** 142 | * Add the correct font style in Android 4.3-. 143 | */ 144 | 145 | dfn { 146 | font-style: italic; 147 | } 148 | 149 | /** 150 | * Correct the font size and margin on `h1` elements within `section` and 151 | * `article` contexts in Chrome, Firefox, and Safari. 152 | */ 153 | 154 | h1 { 155 | font-size: 2em; 156 | margin: 0.67em 0; 157 | } 158 | 159 | /** 160 | * Add the correct background and color in IE 9-. 161 | */ 162 | 163 | mark { 164 | background-color: #ff0; 165 | color: #000; 166 | } 167 | 168 | /** 169 | * Add the correct font size in all browsers. 170 | */ 171 | 172 | small { 173 | font-size: 80%; 174 | } 175 | 176 | /** 177 | * Prevent `sub` and `sup` elements from affecting the line height in 178 | * all browsers. 179 | */ 180 | 181 | sub, 182 | sup { 183 | font-size: 75%; 184 | line-height: 0; 185 | position: relative; 186 | vertical-align: baseline; 187 | } 188 | 189 | sub { 190 | bottom: -0.25em; 191 | } 192 | 193 | sup { 194 | top: -0.5em; 195 | } 196 | 197 | /* Embedded content 198 | ========================================================================== */ 199 | 200 | /** 201 | * Remove the border on images inside links in IE 10-. 202 | */ 203 | 204 | img { 205 | border-style: none; 206 | } 207 | 208 | /** 209 | * Hide the overflow in IE. 210 | */ 211 | 212 | svg:not(:root) { 213 | overflow: hidden; 214 | } 215 | 216 | /* Grouping content 217 | ========================================================================== */ 218 | 219 | /** 220 | * 1. Correct the inheritance and scaling of font size in all browsers. 221 | * 2. Correct the odd `em` font sizing in all browsers. 222 | */ 223 | 224 | code, 225 | kbd, 226 | pre, 227 | samp { 228 | font-family: monospace, monospace; /* 1 */ 229 | font-size: 1em; /* 2 */ 230 | } 231 | 232 | /** 233 | * Add the correct margin in IE 8. 234 | */ 235 | 236 | figure { 237 | margin: 1em 40px; 238 | } 239 | 240 | /** 241 | * 1. Add the correct box sizing in Firefox. 242 | * 2. Show the overflow in Edge and IE. 243 | */ 244 | 245 | hr { 246 | box-sizing: content-box; /* 1 */ 247 | height: 0; /* 1 */ 248 | overflow: visible; /* 2 */ 249 | } 250 | 251 | /* Forms 252 | ========================================================================== */ 253 | 254 | /** 255 | * 1. Change font properties to `inherit` in all browsers (opinionated). 256 | * 2. Remove the margin in Firefox and Safari. 257 | */ 258 | 259 | button, 260 | input, 261 | optgroup, 262 | select, 263 | textarea { 264 | font: inherit; /* 1 */ 265 | margin: 0; /* 2 */ 266 | } 267 | 268 | /** 269 | * Restore the font weight unset by the previous rule. 270 | */ 271 | 272 | optgroup { 273 | font-weight: bold; 274 | } 275 | 276 | /** 277 | * Show the overflow in IE. 278 | * 1. Show the overflow in Edge. 279 | */ 280 | 281 | button, 282 | input { /* 1 */ 283 | overflow: visible; 284 | } 285 | 286 | /** 287 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 288 | * 1. Remove the inheritance of text transform in Firefox. 289 | */ 290 | 291 | button, 292 | select { /* 1 */ 293 | text-transform: none; 294 | } 295 | 296 | /** 297 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 298 | * controls in Android 4. 299 | * 2. Correct the inability to style clickable types in iOS and Safari. 300 | */ 301 | 302 | button, 303 | html [type="button"], /* 1 */ 304 | [type="reset"], 305 | [type="submit"] { 306 | -webkit-appearance: button; /* 2 */ 307 | } 308 | 309 | /** 310 | * Remove the inner border and padding in Firefox. 311 | */ 312 | 313 | button::-moz-focus-inner, 314 | [type="button"]::-moz-focus-inner, 315 | [type="reset"]::-moz-focus-inner, 316 | [type="submit"]::-moz-focus-inner { 317 | border-style: none; 318 | padding: 0; 319 | } 320 | 321 | /** 322 | * Restore the focus styles unset by the previous rule. 323 | */ 324 | 325 | button:-moz-focusring, 326 | [type="button"]:-moz-focusring, 327 | [type="reset"]:-moz-focusring, 328 | [type="submit"]:-moz-focusring { 329 | outline: 1px dotted ButtonText; 330 | } 331 | 332 | /** 333 | * Change the border, margin, and padding in all browsers (opinionated). 334 | */ 335 | 336 | fieldset { 337 | border: 1px solid #c0c0c0; 338 | margin: 0 2px; 339 | padding: 0.35em 0.625em 0.75em; 340 | } 341 | 342 | /** 343 | * 1. Correct the text wrapping in Edge and IE. 344 | * 2. Correct the color inheritance from `fieldset` elements in IE. 345 | * 3. Remove the padding so developers are not caught out when they zero out 346 | * `fieldset` elements in all browsers. 347 | */ 348 | 349 | legend { 350 | box-sizing: border-box; /* 1 */ 351 | color: inherit; /* 2 */ 352 | display: table; /* 1 */ 353 | max-width: 100%; /* 1 */ 354 | padding: 0; /* 3 */ 355 | white-space: normal; /* 1 */ 356 | } 357 | 358 | /** 359 | * Remove the default vertical scrollbar in IE. 360 | */ 361 | 362 | textarea { 363 | overflow: auto; 364 | } 365 | 366 | /** 367 | * 1. Add the correct box sizing in IE 10-. 368 | * 2. Remove the padding in IE 10-. 369 | */ 370 | 371 | [type="checkbox"], 372 | [type="radio"] { 373 | box-sizing: border-box; /* 1 */ 374 | padding: 0; /* 2 */ 375 | } 376 | 377 | /** 378 | * Correct the cursor style of increment and decrement buttons in Chrome. 379 | */ 380 | 381 | [type="number"]::-webkit-inner-spin-button, 382 | [type="number"]::-webkit-outer-spin-button { 383 | height: auto; 384 | } 385 | 386 | /** 387 | * 1. Correct the odd appearance in Chrome and Safari. 388 | * 2. Correct the outline style in Safari. 389 | */ 390 | 391 | [type="search"] { 392 | -webkit-appearance: textfield; /* 1 */ 393 | outline-offset: -2px; /* 2 */ 394 | } 395 | 396 | /** 397 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 398 | */ 399 | 400 | [type="search"]::-webkit-search-cancel-button, 401 | [type="search"]::-webkit-search-decoration { 402 | -webkit-appearance: none; 403 | } 404 | 405 | /** 406 | * Correct the text style of placeholders in Chrome, Edge, and Safari. 407 | */ 408 | 409 | ::-webkit-input-placeholder { 410 | color: inherit; 411 | opacity: 0.54; 412 | } 413 | 414 | /** 415 | * 1. Correct the inability to style clickable types in iOS and Safari. 416 | * 2. Change font properties to `inherit` in Safari. 417 | */ 418 | 419 | ::-webkit-file-upload-button { 420 | -webkit-appearance: button; /* 1 */ 421 | font: inherit; /* 2 */ 422 | } 423 | -------------------------------------------------------------------------------- /examples/fixed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | jQuery asScrollable 7 | 8 | 9 | 10 | 11 | 12 | 43 | 44 | 45 | 48 | 55 |
56 |
57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 |
77 |
78 |
79 |
80 | 81 | 82 | 83 | 84 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | jQuery asScrollable 7 | 8 | 9 | 10 | 11 | 12 | 40 | 41 | 42 | 45 |
46 |
47 |
48 |

Vertical Scrollable

49 |
50 |
51 |
52 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam malesuada at metus eget sodales. Aenean tincidunt mi sed orci sollicitudin placerat. Nullam tempus nisl augue, sed pulvinar eros lacinia vitae. Mauris vehicula velit a nibh dapibus vehicula. Fusce dui tellus, tincidunt sit amet porttitor efficitur, aliquam ac arcu. Cras non tempor dui. Nunc a dolor sit amet dolor bibendum auctor a eu ipsum.

53 |

Pellentesque lobortis facilisis risus, sit amet maximus turpis venenatis vitae. Donec nec eros iaculis, congue risus at, tempus augue. Donec quis felis vel purus pretium tincidunt. Integer sodales ultricies tristique. Phasellus et risus sagittis, dictum tortor a, semper lorem. Vivamus quis ipsum velit. Nam molestie ut ipsum ultricies volutpat. Integer molestie sagittis tempor. Integer vitae mauris est. Ut laoreet dignissim tellus, non accumsan erat gravida vel. Curabitur non erat id velit aliquam malesuada. Proin aliquet cursus orci quis pulvinar.

54 |

Duis elit massa, scelerisque sed nisl sed, tempus iaculis felis. Duis accumsan eget justo id auctor. Aliquam consequat odio non dolor efficitur, hendrerit porttitor neque porttitor. Integer varius maximus nunc, at malesuada leo tristique id. Sed aliquet pharetra ipsum, non interdum lacus dictum sit amet. Curabitur semper imperdiet sem eget interdum. Nunc at egestas tellus, vel tincidunt lacus. Fusce eget neque vel leo volutpat tincidunt ac non enim. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus cursus leo in felis viverra interdum.

55 |

Mauris id ultricies magna. Quisque rutrum lobortis elit blandit rutrum. Curabitur mattis enim lorem, eget tempor nunc pretium dignissim. Cras tincidunt ac nisl eget finibus. Fusce lobortis turpis sed dui mollis, eget fringilla risus porttitor. In id neque vitae lorem pharetra sodales. Morbi neque ex, mattis at dolor quis, consequat tincidunt leo. Vivamus sagittis placerat sem at porta.

56 |

Proin varius arcu ac ligula suscipit, sit amet pretium lectus tincidunt. Aliquam eu mi imperdiet, efficitur tellus ac, mollis eros. Cras malesuada feugiat pharetra. Curabitur lectus lacus, bibendum sed odio at, egestas tempor nisl. Vivamus sagittis est porta velit pretium, in elementum arcu tempus. Ut id cursus libero, non ullamcorper velit. Cras pretium arcu lacus, nec dignissim elit accumsan vitae. Aliquam tristique lorem et tempus congue. Donec vel metus enim. Praesent sed turpis et magna suscipit tincidunt. Proin efficitur neque non sapien cursus vehicula. Suspendisse iaculis, neque vel convallis lobortis, mauris dui posuere mi, at maximus lorem tortor a metus. Ut in quam efficitur, finibus nulla et, feugiat orci.

57 |

Sed mattis volutpat enim eget porttitor. Sed lectus ligula, condimentum nec elit eget, vehicula porttitor nunc. Curabitur pulvinar leo velit, a convallis tellus suscipit ac. Donec tempor est ut sagittis varius. Mauris maximus nunc metus, non venenatis justo ornare vel. Phasellus iaculis erat sit amet enim fermentum mattis. Cras sollicitudin tortor dolor, ac aliquam dolor iaculis non. Donec sed sodales enim. Suspendisse potenti. Integer quis turpis cursus enim consectetur fringilla eu eu ante.

58 |
59 |
60 |
61 |
62 |
63 |

Horizontal Scrollable

64 |
65 |
66 |
67 | 68 |
69 |
70 |
71 |
72 |
73 |

Both Scrollable

74 |
75 |
76 |
77 | 78 |
79 |
80 |
81 |
82 |
83 |

Both Scrollable With No scroll

84 |
85 |
86 |
87 | 88 |
89 |
90 |
91 |
92 |
93 |

Auto x

94 |
95 |
96 |
97 | 98 |
99 |
100 |
101 |
102 |
103 |

Auto y

104 |
105 |
106 |
107 | 108 |
109 |
110 |
111 |
112 |
113 |

Auto both

114 |
115 |
116 |
117 | 118 |
119 |
120 |
121 |
122 |
123 |

Auto none

124 |
125 |
126 |
127 | 128 |
129 |
130 |
131 |
132 |
133 |

Simple Structure

134 |
135 |
136 |
137 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam malesuada at metus eget sodales. Aenean tincidunt mi sed orci sollicitudin placerat. Nullam tempus nisl augue, sed pulvinar eros lacinia vitae. Mauris vehicula velit a nibh dapibus vehicula. Fusce dui tellus, tincidunt sit amet porttitor efficitur, aliquam ac arcu. Cras non tempor dui. Nunc a dolor sit amet dolor bibendum auctor a eu ipsum.

138 |

Pellentesque lobortis facilisis risus, sit amet maximus turpis venenatis vitae. Donec nec eros iaculis, congue risus at, tempus augue. Donec quis felis vel purus pretium tincidunt. Integer sodales ultricies tristique. Phasellus et risus sagittis, dictum tortor a, semper lorem. Vivamus quis ipsum velit. Nam molestie ut ipsum ultricies volutpat. Integer molestie sagittis tempor. Integer vitae mauris est. Ut laoreet dignissim tellus, non accumsan erat gravida vel. Curabitur non erat id velit aliquam malesuada. Proin aliquet cursus orci quis pulvinar.

139 |

Duis elit massa, scelerisque sed nisl sed, tempus iaculis felis. Duis accumsan eget justo id auctor. Aliquam consequat odio non dolor efficitur, hendrerit porttitor neque porttitor. Integer varius maximus nunc, at malesuada leo tristique id. Sed aliquet pharetra ipsum, non interdum lacus dictum sit amet. Curabitur semper imperdiet sem eget interdum. Nunc at egestas tellus, vel tincidunt lacus. Fusce eget neque vel leo volutpat tincidunt ac non enim. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus cursus leo in felis viverra interdum.

140 |

Mauris id ultricies magna. Quisque rutrum lobortis elit blandit rutrum. Curabitur mattis enim lorem, eget tempor nunc pretium dignissim. Cras tincidunt ac nisl eget finibus. Fusce lobortis turpis sed dui mollis, eget fringilla risus porttitor. In id neque vitae lorem pharetra sodales. Morbi neque ex, mattis at dolor quis, consequat tincidunt leo. Vivamus sagittis placerat sem at porta.

141 |

Proin varius arcu ac ligula suscipit, sit amet pretium lectus tincidunt. Aliquam eu mi imperdiet, efficitur tellus ac, mollis eros. Cras malesuada feugiat pharetra. Curabitur lectus lacus, bibendum sed odio at, egestas tempor nisl. Vivamus sagittis est porta velit pretium, in elementum arcu tempus. Ut id cursus libero, non ullamcorper velit. Cras pretium arcu lacus, nec dignissim elit accumsan vitae. Aliquam tristique lorem et tempus congue. Donec vel metus enim. Praesent sed turpis et magna suscipit tincidunt. Proin efficitur neque non sapien cursus vehicula. Suspendisse iaculis, neque vel convallis lobortis, mauris dui posuere mi, at maximus lorem tortor a metus. Ut in quam efficitur, finibus nulla et, feugiat orci.

142 |

Sed mattis volutpat enim eget porttitor. Sed lectus ligula, condimentum nec elit eget, vehicula porttitor nunc. Curabitur pulvinar leo velit, a convallis tellus suscipit ac. Donec tempor est ut sagittis varius. Mauris maximus nunc metus, non venenatis justo ornare vel. Phasellus iaculis erat sit amet enim fermentum mattis. Cras sollicitudin tortor dolor, ac aliquam dolor iaculis non. Donec sed sodales enim. Suspendisse potenti. Integer quis turpis cursus enim consectetur fringilla eu eu ante.

143 |
144 |
145 |
146 |
147 |
148 |
149 | 150 | 151 | 152 | 153 | 154 |
155 |
156 | 157 | 158 | 159 | 160 | 161 | 162 |
163 |
164 | 165 | 166 | 167 | 168 |
169 |
170 |
171 |
172 | 173 | 174 | 175 | 176 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /gulp/tasks/archive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import zip from 'gulp-zip'; 6 | import notify from 'gulp-notify'; 7 | 8 | export default function (src = config.archive.src, dest = config.archive.dest, message = 'Archive task complete') { 9 | return function () { 10 | return gulp.src(src) 11 | .pipe(zip(`${config.version}.zip`)) 12 | .pipe(gulp.dest(dest)) 13 | .pipe(notify({ 14 | title: config.notify.title, 15 | message: message 16 | })); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /gulp/tasks/assets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import merge from 'merge-stream'; 5 | import gulp from 'gulp'; 6 | import browser from 'browser-sync'; 7 | import notify from 'gulp-notify'; 8 | import AssetsManager from 'assets-manager'; 9 | import {argv} from 'yargs'; 10 | 11 | function getPackage() { 12 | if (argv.package) { 13 | return argv.package; 14 | } 15 | return null; 16 | } 17 | 18 | /* 19 | * Checkout https://github.com/amazingSurge/assets-manager 20 | */ 21 | export function copy(options = config.assets, message = 'Assets task complete') { 22 | return function (done) { 23 | let pkgName = getPackage(); 24 | const manager = new AssetsManager('manifest.json', options); 25 | 26 | if(pkgName) { 27 | manager.copyPackage(pkgName).then(()=>{ 28 | done(); 29 | }); 30 | } else { 31 | manager.copyPackages().then(()=>{ 32 | done(); 33 | }); 34 | } 35 | } 36 | } 37 | 38 | export function clean(options = config.assets, message = 'Assets clean task complete') { 39 | return function (done) { 40 | let pkgName = getPackage(); 41 | const manager = new AssetsManager('manifest.json', options); 42 | 43 | if(pkgName) { 44 | manager.cleanPackage(pkgName).then(()=>{ 45 | done(); 46 | }); 47 | } else { 48 | manager.cleanPackages().then(()=>{ 49 | done(); 50 | }); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /gulp/tasks/browser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import browser from 'browser-sync'; 5 | import notifier from 'node-notifier'; 6 | 7 | export function init(options = {}, message = 'Browser starting') { 8 | options = Object.assign(options, { 9 | server: { 10 | baseDir: config.browser.baseDir, 11 | }, 12 | startPath: config.browser.startPath, 13 | port: config.browser.browserPort, 14 | ui: { 15 | port: config.browser.UIPort 16 | }, 17 | ghostMode: { 18 | links: false 19 | } 20 | }); 21 | 22 | return function() { 23 | browser.init(options, () => { 24 | notifier.notify({ 25 | title: config.notify.title, 26 | message: message 27 | }); 28 | }); 29 | }; 30 | } 31 | 32 | export function reload(message = 'Browser reloaded') { 33 | return function(done) { 34 | browser.reload(); 35 | done(); 36 | 37 | notifier.notify({ 38 | title: config.notify.title, 39 | message: message 40 | }); 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /gulp/tasks/clean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import del from 'del'; 5 | 6 | export default function (src = config.paths.destDir) { 7 | return function (done) { 8 | del.sync([src]); 9 | 10 | done(); 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /gulp/tasks/deploy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import inquirer from 'inquirer'; 6 | import replace from 'gulp-replace'; 7 | import { execSync as exec, spawnSync as spawn } from 'child_process'; 8 | import semver from 'semver'; 9 | import gutil from 'gulp-util'; 10 | 11 | const CURRENT_VERSION = config.version; 12 | let NEXT_VERSION; 13 | let NEXT_MESSAGE; 14 | 15 | export function prompt(done) { 16 | inquirer.prompt([{ 17 | type: 'input', 18 | name: 'version', 19 | message: `What version are we moving to? (Current version is ${CURRENT_VERSION})`, 20 | validate: function (input) { 21 | if(input === '') { 22 | input = CURRENT_VERSION; 23 | } 24 | return /^\d*[\d.]*\d*$/.test(input); 25 | } 26 | }]).then((answers) => { 27 | if (answers.version === '') { 28 | NEXT_VERSION = semver.inc(CURRENT_VERSION, config.deploy.increment); 29 | gutil.log(gutil.colors.green(`No version inputted, bump to version ${NEXT_VERSION}`)); 30 | } else { 31 | NEXT_VERSION = answers.version; 32 | } 33 | 34 | done(); 35 | }); 36 | } 37 | 38 | export function message(done) { 39 | inquirer.prompt([{ 40 | type: 'input', 41 | name: 'message', 42 | message: `What message are we going to commit?`, 43 | validate: function (input) { 44 | if(input === '' && NEXT_VERSION === CURRENT_VERSION) { 45 | return false; 46 | } 47 | return true; 48 | } 49 | }]).then((answers) => { 50 | if (answers.message !== '') { 51 | NEXT_MESSAGE = answers.message; 52 | } else { 53 | NEXT_MESSAGE = ''; 54 | } 55 | done(); 56 | }); 57 | } 58 | 59 | // Bumps the version number in any file that has one 60 | export function version() { 61 | return gulp.src(config.deploy.versionFiles, { 62 | base: process.cwd() 63 | }) 64 | // .pipe(replace(CURRENT_VERSION, NEXT_VERSION)) 65 | .pipe(replace(/Version\s*:\s*([\d.]+)/, `Version: ${NEXT_VERSION}`)) 66 | .pipe(replace(/("|')version\1\s*:\s*("|')([\d.]+)\2/, `$1version$1: $2${NEXT_VERSION}$2`)) 67 | .pipe(gulp.dest('.')); 68 | } 69 | 70 | export function init(done) { 71 | config.production = true; 72 | config.init(); 73 | 74 | done(); 75 | } 76 | 77 | // Writes a commit with the changes to the version numbers 78 | export function commit(done) { 79 | let message = `Release ${NEXT_VERSION}`; 80 | 81 | if (NEXT_VERSION === CURRENT_VERSION) { 82 | message = NEXT_MESSAGE; 83 | } else if(NEXT_MESSAGE !== '') { 84 | message = `${message}; ${NEXT_MESSAGE}`; 85 | } 86 | exec('git add .'); 87 | exec(`git commit -am "${message}"`); 88 | 89 | if (NEXT_VERSION !== CURRENT_VERSION) { 90 | exec(`git tag v${NEXT_VERSION}`); 91 | } 92 | 93 | exec('git push origin master --follow-tags'); 94 | done(); 95 | } 96 | -------------------------------------------------------------------------------- /gulp/tasks/lint-scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import eslint from 'gulp-eslint'; 6 | import getSrcFiles from '../util/getSrcFiles'; 7 | 8 | export function es(src = config.scripts.src, options = {}, files = ['**/*.js', '!**/*.min.js']) { 9 | return function() { 10 | let srcFiles = getSrcFiles(src, files); 11 | 12 | options = Object.assign({ 13 | useEslintrc: true, 14 | configFile: '.eslintrc.json', 15 | fix: true 16 | }, options); 17 | 18 | return gulp.src(srcFiles, { 19 | base: './' 20 | }) 21 | .pipe(eslint(options)) 22 | .pipe(eslint.format()) 23 | .pipe(eslint.failAfterError()); 24 | }; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /gulp/tasks/lint-styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import stylelint from 'gulp-stylelint'; 6 | import getSrcFiles from '../util/getSrcFiles'; 7 | 8 | export function style(src = config.styles.src, files = '**/*.scss') { 9 | return function() { 10 | let srcFiles = getSrcFiles(src, files); 11 | 12 | return gulp.src(srcFiles) 13 | .pipe(stylelint({ 14 | reporters: [{ 15 | formatter: 'string', 16 | console: true 17 | }] 18 | })); 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /gulp/tasks/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import releaseIt from 'release-it'; 5 | import handleErrors from '../util/handleErrors'; 6 | import {argv} from 'yargs'; 7 | 8 | export default function release() { 9 | let options = {}; 10 | options.increment = argv.increment || "patch"; 11 | options.verbose = argv.verbose || true; 12 | options.debug = argv.debug || false; 13 | options.force = argv.force || false; 14 | options['dry-run'] = argv['dry-run'] || false; 15 | 16 | config.setEnv('production'); 17 | 18 | return function(done) { 19 | releaseIt.execute(options).catch(handleErrors).finally(done); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /gulp/tasks/scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import babel from 'gulp-babel'; 6 | import gulpif from 'gulp-if'; 7 | import sourcemaps from 'gulp-sourcemaps'; 8 | import handleErrors from '../util/handleErrors'; 9 | import getSrcFiles from '../util/getSrcFiles'; 10 | import browser from 'browser-sync'; 11 | import header from 'gulp-header'; 12 | import rename from 'gulp-rename'; 13 | import rollup from 'gulp-rollup'; 14 | import uglify from 'gulp-uglify'; 15 | import size from 'gulp-size'; 16 | import plumber from 'gulp-plumber'; 17 | import prettier from 'gulp-nf-prettier'; 18 | import path from 'path'; 19 | import notify from 'gulp-notify'; 20 | import replace from 'gulp-replace'; 21 | 22 | export function bundler(src = config.scripts.src, dest = config.scripts.dest, input = config.scripts.input, files = config.scripts.files, message = 'Bundler task complete') { 23 | return function () { 24 | let srcFiles = getSrcFiles(src, files); 25 | 26 | return gulp.src(srcFiles) 27 | .on('error', handleErrors) 28 | .pipe(plumber({errorHandler: handleErrors})) 29 | .pipe(rollup({ 30 | input: `${src}/${input}`, 31 | format: 'es', 32 | globals: { 33 | jquery: 'jQuery' 34 | } 35 | })) 36 | .pipe(header(config.banner)) 37 | .pipe(rename({ 38 | basename: config.name, 39 | suffix: '.es' 40 | })) 41 | .pipe(gulp.dest(dest)) 42 | .pipe(notify({ 43 | title: config.notify.title, 44 | message: message, 45 | onLast: true 46 | })); 47 | }; 48 | } 49 | 50 | export function scripts(src = config.scripts.src, dest = config.scripts.dest, input = config.scripts.input, files = config.scripts.files, message = 'Scripts task complete') { 51 | const createSourcemap = config.deploy || config.scripts.prodSourcemap; 52 | 53 | return function () { 54 | let srcFiles = getSrcFiles(src, files); 55 | 56 | return gulp.src(`${dest}/${config.name}.es.js`) 57 | .on('error', handleErrors) 58 | .pipe(plumber({errorHandler: handleErrors})) 59 | // .pipe(rollup({ 60 | // input: `${src}/${input}`, 61 | // })) 62 | .pipe(babel({ 63 | "presets": ["es2015"], 64 | "plugins": [ 65 | ["transform-es2015-modules-umd", { 66 | "globals": { 67 | "jquery": "jQuery" 68 | } 69 | }] 70 | ] 71 | })) 72 | .pipe(header(config.banner)) 73 | .pipe( 74 | prettier({ 75 | parser: 'flow', 76 | tabWidth: 2, 77 | useTabs: false, 78 | semi: true, 79 | singleQuote: true, 80 | bracketSpacing: true, 81 | }) 82 | ) 83 | .pipe(rename({ 84 | basename: config.name 85 | })) 86 | .pipe(gulp.dest(dest)) 87 | .pipe(size({ 88 | title: 'scripts', 89 | showFiles: true 90 | })) 91 | .pipe(rename({ 92 | suffix: '.min' 93 | })) 94 | .pipe(gulpif(createSourcemap, sourcemaps.init())) 95 | .pipe(uglify()) 96 | .pipe(header(config.banner)) 97 | .pipe(gulpif( 98 | createSourcemap, 99 | sourcemaps.write(config.deploy ? './' : null)) 100 | ) 101 | .pipe(gulp.dest(dest)) 102 | .pipe(size({ 103 | title: 'minified scripts', 104 | showFiles: true 105 | })) 106 | .pipe(browser.stream()) 107 | .pipe(notify({ 108 | title: config.notify.title, 109 | message: message, 110 | onLast: true 111 | })); 112 | }; 113 | } 114 | 115 | export function version(src = config.scripts.src, file = config.scripts.version) { 116 | return function() { 117 | return gulp.src(path.join(src, file), {base: "./"}) 118 | .pipe(replace(/("{0,1}|'{0,1})version\1\s*:\s*("|')([\d.]+)\2/, `$1version$1:$2${config.version}$2`)) 119 | .pipe(gulp.dest("./", {overwrite: true})); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /gulp/tasks/styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../config'; 4 | import gulp from 'gulp'; 5 | import gulpif from 'gulp-if'; 6 | import sourcemaps from 'gulp-sourcemaps'; 7 | import sass from 'gulp-sass'; 8 | import sassUnicode from 'gulp-sass-unicode'; 9 | import handleErrors from '../util/handleErrors'; 10 | import browser from 'browser-sync'; 11 | import autoprefixer from 'gulp-autoprefixer'; 12 | import cssnano from 'gulp-cssnano'; 13 | import csscomb from 'gulp-csscomb'; 14 | import rename from 'gulp-rename'; 15 | import header from 'gulp-header'; 16 | import size from 'gulp-size'; 17 | import plumber from 'gulp-plumber'; 18 | import notify from 'gulp-notify'; 19 | import getSrcFiles from '../util/getSrcFiles'; 20 | 21 | export default function (src = config.styles.src, dest = config.styles.dest, files = config.styles.files, message = 'Styles task complete') { 22 | const createSourcemap = config.deploy || config.styles.prodSourcemap; 23 | 24 | return function() { 25 | let srcFiles = getSrcFiles(src, files); 26 | 27 | return gulp.src(srcFiles) 28 | .on('error', handleErrors) 29 | .pipe(plumber({errorHandler: handleErrors})) 30 | .pipe(sass({ 31 | outputStyle: 'nested', 32 | includePaths: config.styles.sassIncludePaths 33 | })) 34 | .pipe(sassUnicode()) 35 | .pipe(csscomb('.csscomb.json')) 36 | .pipe(autoprefixer(config.styles.autoprefixer)) 37 | .pipe(header(config.banner)) 38 | .pipe(gulp.dest(dest)) 39 | .pipe(size({ 40 | title: 'styles', 41 | showFiles: true 42 | })) 43 | .pipe(rename({ 44 | suffix: '.min' 45 | })) 46 | .pipe(gulpif(createSourcemap, sourcemaps.init())) 47 | .pipe(cssnano({ 48 | safe: true, 49 | autoprefixer: false 50 | })) 51 | .pipe(header(config.banner)) 52 | .pipe(gulpif( 53 | createSourcemap, 54 | sourcemaps.write(config.deploy ? './' : null)) 55 | ) 56 | .pipe(gulp.dest(dest)) 57 | .pipe(size({ 58 | title: 'minified styles', 59 | showFiles: true 60 | })) 61 | .pipe(browser.stream()) 62 | .pipe(notify({ 63 | title: config.notify.title, 64 | message: message, 65 | onLast: true 66 | })); 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /gulp/tasks/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { Server as KarmaServer } from 'karma'; 4 | 5 | export default function (options = {}) { 6 | return function(done) { 7 | options = Object.assign({ 8 | configFile: `${__dirname}/../../karma.conf.js`, 9 | }, options); 10 | 11 | let karma = new KarmaServer(options, done); 12 | 13 | karma.start(); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /gulp/util/getFolders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'graceful-fs'; 4 | import path from 'path'; 5 | 6 | export default function(dir) { 7 | return fs.readdirSync(dir).filter((file) => { 8 | return fs.statSync(path.join(dir, file)).isDirectory(); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /gulp/util/getSrcFiles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import {argv} from 'yargs'; 4 | import path from 'path'; 5 | import pathExists from 'path-exists'; 6 | 7 | export default function(src, files, argName = 'file') { 8 | let srcFiles = ''; 9 | 10 | if(argv[argName] && pathExists.sync(path.join(src, argv[argName]))) { 11 | let arg = argv[argName]; 12 | srcFiles = `${src}/${arg}`; 13 | } else if(Array.isArray(files)) { 14 | srcFiles = files.map((file) => { 15 | if(file.indexOf('!') === 0) { 16 | file = file.substr(1); 17 | return `!${src}/${file}`; 18 | } 19 | 20 | return `${src}/${file}`; 21 | }); 22 | } else { 23 | srcFiles = `${src}/${files}`; 24 | } 25 | 26 | return srcFiles; 27 | } 28 | -------------------------------------------------------------------------------- /gulp/util/handleErrors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import gutil from 'gulp-util'; 4 | import config from '../../config'; 5 | import notifier from 'node-notifier'; 6 | 7 | export default function(error) { 8 | if (!config.deploy) { 9 | // Send error to notification center with gulp-notify 10 | notifier.notify({ 11 | title: config.notify.title, 12 | subtitle: 'Failure!', 13 | message: error.message, 14 | }); 15 | gutil.log(gutil.colors.red(error)); 16 | // Keep gulp from hanging on this task 17 | this.emit('end'); 18 | } else { 19 | // Log the error and stop the process 20 | // to prevent broken code from building 21 | gutil.log(gutil.colors.red(error)); 22 | process.exit(1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from './config'; 4 | import fs from 'graceful-fs'; 5 | import gulp from 'gulp'; 6 | import gutil from 'gulp-util'; 7 | 8 | // Tasks 9 | import clean from './gulp/tasks/clean'; 10 | import styles from './gulp/tasks/styles'; 11 | import {version,bundler,scripts} from './gulp/tasks/scripts'; 12 | import * as lintScripts from './gulp/tasks/lint-scripts'; 13 | import * as lintStyles from './gulp/tasks/lint-styles'; 14 | import test from './gulp/tasks/test'; 15 | import * as deploy from './gulp/tasks/deploy'; 16 | import * as browser from './gulp/tasks/browser'; 17 | import * as assets from './gulp/tasks/assets'; 18 | import archive from './gulp/tasks/archive'; 19 | import release from './gulp/tasks/release'; 20 | 21 | if (config.production) { 22 | gutil.log(gutil.colors.bold.green('� Production Mode')); 23 | } else { 24 | gutil.log(gutil.colors.bold.green('� Development Mode')); 25 | } 26 | 27 | gulp.task('version', version()); 28 | gulp.task('bundler', bundler()); 29 | gulp.task('scripts', scripts()); 30 | gulp.task('clean', clean(config.scripts.dest)); 31 | 32 | // Styles 33 | gulp.task('styles', styles()); 34 | gulp.task('clean:styles', clean(config.styles.dest)); 35 | 36 | // Build the files 37 | gulp.task('build', gulp.series('clean', 'version', 'bundler', 'scripts', 'styles')); 38 | 39 | // Assets 40 | gulp.task('assets', assets.copy()); 41 | gulp.task('clean:assets', assets.clean()); 42 | 43 | // Lint Styles 44 | gulp.task('lint:style', lintStyles.style()); 45 | 46 | // Lint Scripts 47 | gulp.task('lint:script:src', lintScripts.es(config.scripts.src)); 48 | gulp.task('lint:script:dest', lintScripts.es(config.scripts.dest)); 49 | gulp.task('lint:script:test', lintScripts.es(config.scripts.test)); 50 | gulp.task('lint:script:gulp', lintScripts.es(config.scripts.gulp, {rules: {'no-console': 'off'}})); 51 | gulp.task('lint:script', gulp.series('lint:script:src', 'lint:script:dest', 'lint:script:test', 'lint:script:gulp')); 52 | 53 | // Run karma for development, will watch and reload 54 | gulp.task('tdd', test()); 55 | 56 | // Run tests and report for ci 57 | gulp.task('test', test({ 58 | singleRun: true, 59 | browsers: ['PhantomJS'], 60 | reporters: ['mocha'] 61 | })); 62 | 63 | gulp.task('coverage', test({ 64 | singleRun: true, 65 | browsers: ['PhantomJS'], 66 | reporters: ['coverage'], 67 | })); 68 | 69 | // Deploy 70 | gulp.task('deploy:prompt', deploy.prompt); 71 | gulp.task('deploy:version', deploy.version); 72 | gulp.task('deploy:message', deploy.message); 73 | gulp.task('deploy:init', deploy.init); 74 | gulp.task('deploy:commit', deploy.commit); 75 | 76 | // Generates compiled CSS and JS files and puts them in the dist/ folder 77 | gulp.task('deploy:dist', gulp.series('build')); 78 | gulp.task('deploy:prepare', gulp.series('deploy:prompt', 'deploy:version', 'deploy:init', 'deploy:dist')); 79 | gulp.task('deploy', gulp.series('deploy:prompt', 'deploy:version', 'deploy:message', 'deploy:dist', 'deploy:commit')); 80 | 81 | // Archive the distrubution files into package 82 | gulp.task('archive', archive()); 83 | 84 | // Starts a BrowerSync instance 85 | gulp.task('serve', browser.init()); 86 | 87 | // Reload browser 88 | gulp.task('reload', browser.reload()); 89 | 90 | // Watch files for changes 91 | gulp.task('watch', () => { 92 | gulp.watch(config.scripts.src, gulp.series('scripts', 'reload')); 93 | gulp.watch(config.styles.src, gulp.series('styles', 'reload')); 94 | }); 95 | 96 | // Release task 97 | gulp.task('release', release()); 98 | 99 | // Dev task 100 | gulp.task('dev', gulp.series('build', 'serve')); 101 | 102 | // Register default task 103 | gulp.task('default', gulp.series('lint:script:src', 'serve')); 104 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | import babel from 'rollup-plugin-babel'; 3 | import babelrc from 'babelrc-rollup'; 4 | import istanbul from 'rollup-plugin-istanbul'; 5 | import babel_istanbul from 'babel-istanbul'; 6 | 7 | module.exports = function(config) { 8 | config.set({ 9 | 10 | // base path that will be used to resolve all patterns (eg. files, exclude) 11 | basePath: '', 12 | 13 | // frameworks to use 14 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 15 | frameworks: ['mocha', 'sinon-chai'], 16 | 17 | // list of files / patterns to load in the browser 18 | files: [ 19 | 'test/spec/*.js' 20 | ], 21 | 22 | // preprocess matching files before serving them to the browser 23 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 24 | preprocessors: { 25 | // "dist/**/*.es.js": ["rollup"], 26 | "src/**/*.js": ["rollup", "coverage"], 27 | "test/spec/**/*.spec.js": ["rollup"] 28 | }, 29 | 30 | // optionally, configure the reporter 31 | // text displays it within the console (alternative: text-summary) 32 | // lcov creates a codecov compatible report 33 | coverageReporter: { 34 | reporters: [ 35 | {'type': 'text'}, 36 | {'type': 'html', dir: 'coverage'}, 37 | {'type': 'lcov'} 38 | ] 39 | }, 40 | 41 | // list of files to exclude 42 | exclude: [ 43 | ], 44 | 45 | 46 | // test results reporter to use 47 | // possible values: 'dots', 'progress' 48 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 49 | // coverage is from karma-coverage and provides Istanbul code coverage report 50 | reporters: ['mocha', 'coverage'], 51 | 52 | 53 | // web server port 54 | port: 9876, 55 | 56 | 57 | // enable / disable colors in the output (reporters and logs) 58 | colors: true, 59 | 60 | 61 | // level of logging 62 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 63 | logLevel: config.LOG_INFO, 64 | 65 | 66 | // enable / disable watching file and executing tests whenever any file changes 67 | autoWatch: true, 68 | 69 | 70 | // start these browsers 71 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 72 | // Currently available: 73 | // - Chrome 74 | // - ChromeCanary 75 | // - Firefox 76 | // - Opera 77 | // - Safari (only Mac) 78 | // - PhantomJS 79 | // - IE (only Windows) 80 | browsers: ['Firefox'], 81 | 82 | // Which plugins to enable 83 | plugins: [ 84 | 'karma-mocha', 85 | 'karma-sinon-chai', 86 | 'karma-chrome-launcher', 87 | 'karma-firefox-launcher', 88 | 'karma-phantomjs-launcher', 89 | 'karma-mocha-reporter', 90 | 'karma-coverage', 91 | 'karma-rollup-plugin' 92 | ], 93 | 94 | rollupPreprocessor: { 95 | plugins: [ 96 | babel(babelrc()), 97 | istanbul({ 98 | include: ['src/**/*.js'], 99 | exclude: ['test/spec/**/*.spec.js', 'node_modules/**'], 100 | // instrumenter: babel_istanbul 101 | }), 102 | ], 103 | sourceMap: 'inline' 104 | }, 105 | 106 | // Continuous Integration mode 107 | // if true, Karma captures browsers, runs the tests and exits 108 | singleRun: false, 109 | 110 | // Concurrency level 111 | // how many browser should be started simultaneous 112 | concurrency: Infinity 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dest": "examples", 3 | "packages": { 4 | "npm:jquery": true, 5 | "npm:jquery-asScrollbar": true, 6 | "npm:normalize.css": true, 7 | "npm:holderjs": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-asScrollable", 3 | "title": "jQuery asScrollable", 4 | "description": "A jquery plugin that make a block element scrollable.", 5 | "version": "0.4.10", 6 | "homepage": "https://github.com/amazingSurge/jquery-asScrollable", 7 | "author": { 8 | "name": "amazingSurge", 9 | "email": "amazingSurge@gmail.com", 10 | "url": "amazingSurge.com" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@github.com:amazingSurge/jquery-asScrollable.git" 15 | }, 16 | "keywords": [ 17 | "jquery", 18 | "jquery-plugin", 19 | "ecosystem:jquery", 20 | "ui", 21 | "es6", 22 | "scrollable", 23 | "scrollbar" 24 | ], 25 | "bugs": "https://github.com/amazingSurge/jquery-asScrollable/issues", 26 | "licenses": [ 27 | { 28 | "type": "LGPL-3.0", 29 | "url": "https://github.com/amazingSurge/jquery-asScrollable/blob/master/LICENSE" 30 | } 31 | ], 32 | "license": "LGPL-3.0", 33 | "main": "dist/jquery-asScrollable.js", 34 | "module": "dist/jquery-asScrollable.es.js", 35 | "files": [ 36 | "dist", 37 | "src" 38 | ], 39 | "scripts": { 40 | "prestart": "npm install", 41 | "start": "gulp serve", 42 | "build": "npm run prestart && gulp build", 43 | "deploy": "gulp deploy", 44 | "deploy:prepare": "gulp deploy:prepare", 45 | "release": "gulp release", 46 | "test": "gulp test" 47 | }, 48 | "devDependencies": { 49 | "yargs": "*", 50 | "assets-manager": "*", 51 | "babel-core": "*", 52 | "babel-eslint": "*", 53 | "babel-istanbul": "*", 54 | "babel-plugin-transform-es2015-modules-umd": "*", 55 | "babel-preset-es2015": "*", 56 | "babel-preset-es2015-rollup": "*", 57 | "babelrc-rollup": "*", 58 | "browser-sync": "*", 59 | "chai": "*", 60 | "del": "*", 61 | "prettier": "*", 62 | "gulp-nf-prettier": "*", 63 | "graceful-fs": "*", 64 | "gulp": "github:gulpjs/gulp#4.0", 65 | "gulp-autoprefixer": "*", 66 | "gulp-babel": "*", 67 | "gulp-changed": "*", 68 | "gulp-csscomb": "*", 69 | "gulp-cssnano": "*", 70 | "gulp-eslint": "*", 71 | "gulp-extname": "*", 72 | "gulp-filter": "*", 73 | "gulp-header": "*", 74 | "gulp-iconfont-css": "*", 75 | "gulp-if": "*", 76 | "gulp-notify": "*", 77 | "gulp-plumber": "*", 78 | "gulp-rename": "*", 79 | "gulp-replace": "*", 80 | "gulp-rollup": "*", 81 | "gulp-sass": "*", 82 | "gulp-sass-unicode": "*", 83 | "gulp-size": "*", 84 | "gulp-sourcemaps": "*", 85 | "gulp-stylelint": "*", 86 | "gulp-uglify": "*", 87 | "gulp-util": "*", 88 | "gulp-zip": "*", 89 | "holderjs": "^2.9.4", 90 | "inquirer": "*", 91 | "semver": "*", 92 | "karma": "*", 93 | "karma-babel-preprocessor": "*", 94 | "karma-chrome-launcher": "*", 95 | "karma-commonjs": "*", 96 | "karma-coverage": "*", 97 | "karma-firefox-launcher": "*", 98 | "karma-mocha": "*", 99 | "karma-mocha-reporter": "*", 100 | "karma-phantomjs-launcher": "*", 101 | "karma-rollup-plugin": "*", 102 | "karma-sinon-chai": "*", 103 | "merge-stream": "*", 104 | "mkdirp": "*", 105 | "mocha": "*", 106 | "node-notifier": "*", 107 | "normalize.css": "^7.0.0", 108 | "path-exists": "*", 109 | "phantomjs-prebuilt": "*", 110 | "release-it": "*", 111 | "rollup-plugin-babel": "*", 112 | "rollup-plugin-istanbul": "*", 113 | "sinon": "*", 114 | "sinon-chai": "*", 115 | "stylelint-config-bootstrap": "*", 116 | "stylelint-scss": "*", 117 | "through2": "*" 118 | }, 119 | "engines": { 120 | "node": ">= 6.2.2", 121 | "npm": ">= 3" 122 | }, 123 | "dependencies": { 124 | "jquery": ">=2.2.0", 125 | "jquery-asScrollbar": "^0.5.6" 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/asScrollable.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import DEFAULTS from './defaults'; 3 | import * as util from './util'; 4 | 5 | const NAMESPACE = 'asScrollable'; 6 | 7 | let instanceId = 0; 8 | 9 | class AsScrollable { 10 | constructor(element, options) { 11 | this.$element = $(element); 12 | options = this.options = $.extend({}, DEFAULTS, options || {}, this.$element.data('options') || {}); 13 | 14 | this.classes = { 15 | wrap: options.namespace, 16 | content: `${options.namespace}-content`, 17 | container: `${options.namespace}-container`, 18 | bar: `${options.namespace}-bar`, 19 | barHide: `${options.namespace}-bar-hide`, 20 | skin: options.skin 21 | }; 22 | 23 | this.attributes = { 24 | vertical: { 25 | axis: 'Y', 26 | overflow: 'overflow-y', 27 | 28 | scroll: 'scrollTop', 29 | scrollLength: 'scrollHeight', 30 | pageOffset: 'pageYOffset', 31 | 32 | ffPadding: 'padding-right', 33 | 34 | length: 'height', 35 | clientLength: 'clientHeight', 36 | offset: 'offsetHeight', 37 | 38 | crossLength: 'width', 39 | crossClientLength: 'clientWidth', 40 | crossOffset: 'offsetWidth' 41 | }, 42 | horizontal: { 43 | axis: 'X', 44 | overflow: 'overflow-x', 45 | 46 | scroll: 'scrollLeft', 47 | scrollLength: 'scrollWidth', 48 | pageOffset: 'pageXOffset', 49 | 50 | ffPadding: 'padding-bottom', 51 | 52 | length: 'width', 53 | clientLength: 'clientWidth', 54 | offset: 'offsetWidth', 55 | 56 | crossLength: 'height', 57 | crossClientLength: 'clientHeight', 58 | crossOffset: 'offsetHeight' 59 | } 60 | }; 61 | 62 | // Current state information. 63 | this._states = {}; 64 | 65 | // Supported direction 66 | this.horizontal = null; 67 | this.vertical = null; 68 | 69 | this.$bar = null; 70 | 71 | // Current timeout 72 | this._frameId = null; 73 | this._timeoutId = null; 74 | 75 | this.instanceId = (++instanceId); 76 | 77 | this.easing = $.asScrollbar.getEasing(this.options.easing) || $.asScrollbar.getEasing('ease'); 78 | 79 | this.init(); 80 | } 81 | 82 | init() { 83 | let position = this.$element.css('position'); 84 | 85 | if (this.options.containerSelector) { 86 | this.$container = this.$element.find(this.options.containerSelector); 87 | this.$wrap = this.$element; 88 | 89 | if (position === 'static') { 90 | this.$wrap.css('position', 'relative'); 91 | } 92 | } else { 93 | this.$container = this.$element.wrap('
'); 94 | this.$wrap = this.$container.parent(); 95 | this.$wrap.height(this.$element.height()); 96 | 97 | if (position !== 'static') { 98 | this.$wrap.css('position', position); 99 | } else { 100 | this.$wrap.css('position', 'relative'); 101 | } 102 | } 103 | 104 | if (this.options.contentSelector) { 105 | this.$content = this.$container.find(this.options.contentSelector); 106 | } else { 107 | this.$content = this.$container.wrap('
'); 108 | this.$container = this.$content.parent(); 109 | } 110 | 111 | switch (this.options.direction) { 112 | case 'vertical': { 113 | this.vertical = true; 114 | break; 115 | } 116 | case 'horizontal': { 117 | this.horizontal = true; 118 | break; 119 | } 120 | case 'both': { 121 | this.horizontal = true; 122 | this.vertical = true; 123 | break; 124 | } 125 | case 'auto': { 126 | let overflowX = this.$element.css('overflow-x'), 127 | overflowY = this.$element.css('overflow-y'); 128 | 129 | if (overflowX === 'scroll' || overflowX === 'auto') { 130 | this.horizontal = true; 131 | } 132 | if (overflowY === 'scroll' || overflowY === 'auto') { 133 | this.vertical = true; 134 | } 135 | break; 136 | } 137 | default: { 138 | break; 139 | } 140 | } 141 | 142 | if (!this.vertical && !this.horizontal) { 143 | return; 144 | } 145 | 146 | this.$wrap.addClass(this.classes.wrap); 147 | this.$container.addClass(this.classes.container); 148 | this.$content.addClass(this.classes.content); 149 | 150 | if (this.options.skin) { 151 | this.$wrap.addClass(this.classes.skin); 152 | } 153 | 154 | this.$wrap.addClass(this.options.enabledClass); 155 | 156 | if (this.vertical) { 157 | this.$wrap.addClass(`${this.classes.wrap}-vertical`); 158 | this.initLayout('vertical'); 159 | this.createBar('vertical'); 160 | } 161 | 162 | if (this.horizontal) { 163 | this.$wrap.addClass(`${this.classes.wrap}-horizontal`); 164 | this.initLayout('horizontal'); 165 | this.createBar('horizontal'); 166 | } 167 | 168 | this.bindEvents(); 169 | 170 | this.trigger('ready'); 171 | } 172 | 173 | bindEvents() { 174 | if (this.options.responsive) { 175 | $(window).on(this.eventNameWithId('orientationchange'), () => { 176 | this.update(); 177 | }); 178 | $(window).on(this.eventNameWithId('resize'), this.throttle(() => { 179 | this.update(); 180 | }, this.options.throttle)); 181 | } 182 | 183 | if (!this.horizontal && !this.vertical) { 184 | return; 185 | } 186 | 187 | let that = this; 188 | 189 | this.$wrap.on(this.eventName('mouseenter'), () => { 190 | that.$wrap.addClass(this.options.hoveringClass); 191 | that.enter('hovering'); 192 | that.trigger('hover'); 193 | }); 194 | 195 | this.$wrap.on(this.eventName('mouseleave'), () => { 196 | that.$wrap.removeClass(this.options.hoveringClass); 197 | 198 | if (!that.is('hovering')) { 199 | return; 200 | } 201 | that.leave('hovering'); 202 | that.trigger('hovered'); 203 | }); 204 | 205 | if (this.options.showOnHover) { 206 | if (this.options.showOnBarHover) { 207 | this.$bar.on('asScrollbar::hover', () => { 208 | if(that.horizontal){ 209 | that.showBar('horizontal'); 210 | } 211 | if(that.vertical){ 212 | that.showBar('vertical'); 213 | } 214 | }).on('asScrollbar::hovered', () => { 215 | if(that.horizontal){ 216 | that.hideBar('horizontal'); 217 | } 218 | if(that.vertical){ 219 | that.hideBar('vertical'); 220 | } 221 | }); 222 | } else { 223 | this.$element.on(`${NAMESPACE}::hover`, $.proxy(this.showBar, this)); 224 | this.$element.on(`${NAMESPACE}::hovered`, $.proxy(this.hideBar, this)); 225 | } 226 | } 227 | 228 | this.$container.on(this.eventName('scroll'), () => { 229 | if (that.horizontal) { 230 | let oldLeft = that.offsetLeft; 231 | that.offsetLeft = that.getOffset('horizontal'); 232 | 233 | if (oldLeft !== that.offsetLeft) { 234 | that.trigger('scroll', that.getPercentOffset('horizontal'), 'horizontal'); 235 | 236 | if (that.offsetLeft === 0) { 237 | that.trigger('scrolltop', 'horizontal'); 238 | } 239 | if (that.offsetLeft === that.getScrollLength('horizontal')) { 240 | that.trigger('scrollend', 'horizontal'); 241 | } 242 | } 243 | } 244 | 245 | if (that.vertical) { 246 | let oldTop = that.offsetTop; 247 | 248 | that.offsetTop = that.getOffset('vertical'); 249 | 250 | if (oldTop !== that.offsetTop) { 251 | that.trigger('scroll', that.getPercentOffset('vertical'), 'vertical'); 252 | 253 | if (that.offsetTop === 0) { 254 | that.trigger('scrolltop', 'vertical'); 255 | } 256 | if (that.offsetTop === that.getScrollLength('vertical')) { 257 | that.trigger('scrollend', 'vertical'); 258 | } 259 | } 260 | } 261 | }); 262 | 263 | this.$element.on(`${NAMESPACE}::scroll`, (e, api, value, direction) => { 264 | if (!that.is('scrolling')) { 265 | that.enter('scrolling'); 266 | that.$wrap.addClass(that.options.scrollingClass); 267 | } 268 | let bar = api.getBarApi(direction); 269 | 270 | bar.moveTo(util.conventToPercentage(value), false, true); 271 | 272 | clearTimeout(that._timeoutId); 273 | that._timeoutId = setTimeout(() => { 274 | that.$wrap.removeClass(that.options.scrollingClass); 275 | that.leave('scrolling'); 276 | }, 200); 277 | }); 278 | 279 | this.$bar.on('asScrollbar::change', (e, api, value) => { 280 | if(typeof e.target.direction === 'string') { 281 | that.scrollTo(e.target.direction, util.conventToPercentage(value), false, true); 282 | } 283 | }); 284 | 285 | this.$bar.on('asScrollbar::drag', () => { 286 | that.$wrap.addClass(that.options.draggingClass); 287 | }).on('asScrollbar::dragged', () => { 288 | that.$wrap.removeClass(that.options.draggingClass); 289 | }); 290 | } 291 | 292 | unbindEvents() { 293 | this.$wrap.off(this.eventName()); 294 | this.$element.off(`${NAMESPACE}::scroll`).off(`${NAMESPACE}::hover`).off(`${NAMESPACE}::hovered`); 295 | this.$container.off(this.eventName()); 296 | $(window).off(this.eventNameWithId()); 297 | } 298 | 299 | initLayout(direction) { 300 | if (direction === 'vertical') { 301 | this.$container.css('height', this.$wrap.height()); 302 | } 303 | let attributes = this.attributes[direction], 304 | container = this.$container[0]; 305 | 306 | // this.$container.css(attributes.overflow, 'scroll'); 307 | 308 | let parentLength = container.parentNode[attributes.crossClientLength], 309 | scrollbarWidth = this.getBrowserScrollbarWidth(direction); 310 | 311 | this.$content.css(attributes.crossLength, `${parentLength}px`); 312 | this.$container.css(attributes.crossLength, `${scrollbarWidth + parentLength}px`); 313 | 314 | if (scrollbarWidth === 0 && util.isFFLionScrollbar) { 315 | this.$container.css(attributes.ffPadding, 16); 316 | } 317 | } 318 | 319 | createBar(direction) { 320 | let options = $.extend(this.options.scrollbar, { 321 | namespace: this.classes.bar, 322 | direction: direction, 323 | useCssTransitions: false, 324 | keyboard: false 325 | }); 326 | let $bar = $('
'); 327 | $bar.asScrollbar(options); 328 | 329 | if (this.options.showOnHover) { 330 | $bar.addClass(this.classes.barHide); 331 | } 332 | 333 | $bar.appendTo(this.$wrap); 334 | 335 | this[`$${direction}`] = $bar; 336 | 337 | if (this.$bar === null) { 338 | this.$bar = $bar; 339 | } else { 340 | this.$bar = this.$bar.add($bar); 341 | } 342 | 343 | this.updateBarHandle(direction); 344 | } 345 | 346 | trigger(eventType, ...params) { 347 | const data = [this].concat(params); 348 | 349 | // event 350 | this.$element.trigger(`${NAMESPACE}::${eventType}`, data); 351 | 352 | // callback 353 | eventType = eventType.replace(/\b\w+\b/g, (word) => { 354 | return word.substring(0, 1).toUpperCase() + word.substring(1); 355 | }); 356 | const onFunction = `on${eventType}`; 357 | 358 | if (typeof this.options[onFunction] === 'function') { 359 | this.options[onFunction].apply(this, params); 360 | } 361 | } 362 | 363 | /** 364 | * Checks whether the carousel is in a specific state or not. 365 | */ 366 | is(state) { 367 | return this._states[state] && this._states[state] > 0; 368 | } 369 | 370 | /** 371 | * Enters a state. 372 | */ 373 | enter(state) { 374 | if (this._states[state] === undefined) { 375 | this._states[state] = 0; 376 | } 377 | 378 | // this._states[state]++; 379 | this._states[state] = 1; 380 | } 381 | 382 | /** 383 | * Leaves a state. 384 | */ 385 | leave(state) { 386 | // this._states[state]--; 387 | 388 | this._states[state] = -1; 389 | } 390 | 391 | eventName(events) { 392 | if (typeof events !== 'string' || events === '') { 393 | return `.${this.options.namespace}`; 394 | } 395 | 396 | events = events.split(' '); 397 | let length = events.length; 398 | for (let i = 0; i < length; i++) { 399 | events[i] = `${events[i]}.${this.options.namespace}`; 400 | } 401 | return events.join(' '); 402 | } 403 | 404 | eventNameWithId(events) { 405 | if (typeof events !== 'string' || events === '') { 406 | return `.${this.options.namespace}-${this.instanceId}`; 407 | } 408 | 409 | events = events.split(' '); 410 | let length = events.length; 411 | for (let i = 0; i < length; i++) { 412 | events[i] = `${events[i]}.${this.options.namespace}-${this.instanceId}`; 413 | } 414 | return events.join(' '); 415 | } 416 | 417 | /** 418 | * _throttle 419 | * @description Borrowed from Underscore.js 420 | */ 421 | throttle(func, wait) { 422 | const _now = Date.now || function() { 423 | return new Date().getTime(); 424 | }; 425 | 426 | let timeout; 427 | let context; 428 | let args; 429 | let result; 430 | let previous = 0; 431 | let later = function() { 432 | previous = _now(); 433 | timeout = null; 434 | result = func.apply(context, args); 435 | if (!timeout) { 436 | context = args = null; 437 | } 438 | }; 439 | 440 | return (...params) => { 441 | /*eslint consistent-this: "off"*/ 442 | let now = _now(); 443 | let remaining = wait - (now - previous); 444 | context = this; 445 | args = params; 446 | if (remaining <= 0 || remaining > wait) { 447 | if (timeout) { 448 | clearTimeout(timeout); 449 | timeout = null; 450 | } 451 | previous = now; 452 | result = func.apply(context, args); 453 | if (!timeout) { 454 | context = args = null; 455 | } 456 | } else if (!timeout) { 457 | timeout = setTimeout(later, remaining); 458 | } 459 | return result; 460 | }; 461 | } 462 | 463 | getBrowserScrollbarWidth(direction) { 464 | let attributes = this.attributes[direction], 465 | outer, outerStyle; 466 | if (attributes.scrollbarWidth) { 467 | return attributes.scrollbarWidth; 468 | } 469 | outer = document.createElement('div'); 470 | outerStyle = outer.style; 471 | outerStyle.position = 'absolute'; 472 | outerStyle.width = '100px'; 473 | outerStyle.height = '100px'; 474 | outerStyle.overflow = 'scroll'; 475 | outerStyle.top = '-9999px'; 476 | document.body.appendChild(outer); 477 | attributes.scrollbarWidth = outer[attributes.offset] - outer[attributes.clientLength]; 478 | document.body.removeChild(outer); 479 | return attributes.scrollbarWidth; 480 | } 481 | 482 | getOffset(direction) { 483 | let attributes = this.attributes[direction], 484 | container = this.$container[0]; 485 | 486 | return (container[attributes.pageOffset] || container[attributes.scroll]); 487 | } 488 | 489 | getPercentOffset(direction) { 490 | return this.getOffset(direction) / this.getScrollLength(direction); 491 | } 492 | 493 | getContainerLength(direction) { 494 | return this.$container[0][this.attributes[direction].clientLength]; 495 | } 496 | 497 | getScrollLength(direction) { 498 | let scrollLength = this.$content[0][this.attributes[direction].scrollLength]; 499 | return scrollLength - this.getContainerLength(direction); 500 | } 501 | 502 | scrollTo(direction, value, trigger, sync) { 503 | let type = typeof value; 504 | 505 | if (type === 'string') { 506 | if (util.isPercentage(value)) { 507 | value = util.convertPercentageToFloat(value) * this.getScrollLength(direction); 508 | } 509 | 510 | value = parseFloat(value); 511 | type = 'number'; 512 | } 513 | 514 | if (type !== 'number') { 515 | return; 516 | } 517 | 518 | this.move(direction, value, trigger, sync); 519 | } 520 | 521 | scrollBy(direction, value, trigger, sync) { 522 | let type = typeof value; 523 | 524 | if (type === 'string') { 525 | if (util.isPercentage(value)) { 526 | value = util.convertPercentageToFloat(value) * this.getScrollLength(direction); 527 | } 528 | 529 | value = parseFloat(value); 530 | type = 'number'; 531 | } 532 | 533 | if (type !== 'number') { 534 | return; 535 | } 536 | 537 | this.move(direction, this.getOffset(direction) + value, trigger, sync); 538 | } 539 | 540 | move(direction, value, trigger, sync) { 541 | if (this[direction] !== true || typeof value !== 'number') { 542 | return; 543 | } 544 | 545 | this.enter('moving'); 546 | 547 | if (value < 0) { 548 | value = 0; 549 | } else if (value > this.getScrollLength(direction)) { 550 | value = this.getScrollLength(direction); 551 | } 552 | 553 | let attributes = this.attributes[direction]; 554 | 555 | var that = this; 556 | let callback = () => { 557 | that.leave('moving'); 558 | }; 559 | 560 | if (sync) { 561 | this.$container[0][attributes.scroll] = value; 562 | 563 | if (trigger !== false) { 564 | this.trigger('change', value / this.getScrollLength(direction), direction); 565 | } 566 | callback(); 567 | } else { 568 | this.enter('animating'); 569 | 570 | let startTime = util.getTime(); 571 | let start = this.getOffset(direction); 572 | let end = value; 573 | 574 | let run = (time) => { 575 | let percent = (time - startTime) / that.options.duration; 576 | 577 | if (percent > 1) { 578 | percent = 1; 579 | } 580 | 581 | percent = that.easing.fn(percent); 582 | 583 | let current = parseFloat(start + percent * (end - start), 10); 584 | that.$container[0][attributes.scroll] = current; 585 | 586 | if (trigger !== false) { 587 | that.trigger('change', value / that.getScrollLength(direction), direction); 588 | } 589 | 590 | if (percent === 1) { 591 | window.cancelAnimationFrame(that._frameId); 592 | that._frameId = null; 593 | 594 | that.leave('animating'); 595 | callback(); 596 | } else { 597 | that._frameId = window.requestAnimationFrame(run); 598 | } 599 | }; 600 | 601 | this._frameId = window.requestAnimationFrame(run); 602 | } 603 | } 604 | 605 | scrollXto(value, trigger, sync) { 606 | return this.scrollTo('horizontal', value, trigger, sync); 607 | } 608 | 609 | scrollYto(value, trigger, sync) { 610 | return this.scrollTo('vertical', value, trigger, sync); 611 | } 612 | 613 | scrollXby(value, trigger, sync) { 614 | return this.scrollBy('horizontal', value, trigger, sync); 615 | } 616 | 617 | scrollYby(value, trigger, sync) { 618 | return this.scrollBy('vertical', value, trigger, sync); 619 | } 620 | 621 | getBar(direction) { 622 | if (direction && this[`$${direction}`]) { 623 | return this[`$${direction}`]; 624 | } 625 | return this.$bar; 626 | } 627 | 628 | getBarApi(direction) { 629 | return this.getBar(direction).data('asScrollbar'); 630 | } 631 | 632 | getBarX() { 633 | return this.getBar('horizontal'); 634 | } 635 | 636 | getBarY() { 637 | return this.getBar('vertical'); 638 | } 639 | 640 | showBar(direction) { 641 | this.getBar(direction).removeClass(this.classes.barHide); 642 | } 643 | 644 | hideBar(direction) { 645 | this.getBar(direction).addClass(this.classes.barHide); 646 | } 647 | 648 | updateBarHandle(direction) { 649 | let api = this.getBarApi(direction); 650 | 651 | if (!api) { 652 | return; 653 | } 654 | 655 | let containerLength = this.getContainerLength(direction), 656 | scrollLength = this.getScrollLength(direction); 657 | 658 | if (scrollLength > 0) { 659 | if (api.is('disabled')) { 660 | api.enable(); 661 | } 662 | api.setHandleLength(api.getBarLength() * containerLength / (scrollLength + containerLength), true); 663 | } else { 664 | 665 | api.disable(); 666 | } 667 | } 668 | 669 | disable() { 670 | if (!this.is('disabled')) { 671 | this.enter('disabled'); 672 | this.$wrap.addClass(this.options.disabledClass).removeClass(this.options.enabledClass); 673 | 674 | this.unbindEvents(); 675 | this.unStyle(); 676 | } 677 | 678 | this.trigger('disable'); 679 | } 680 | 681 | enable() { 682 | if (this.is('disabled')) { 683 | this.leave('disabled'); 684 | this.$wrap.addClass(this.options.enabledClass).removeClass(this.options.disabledClass); 685 | 686 | this.bindEvents(); 687 | this.update(); 688 | } 689 | 690 | this.trigger('enable'); 691 | } 692 | 693 | update() { 694 | if (this.is('disabled')) { 695 | return; 696 | } 697 | if(this.$element.is(':visible')) { 698 | if (this.vertical) { 699 | this.initLayout('vertical'); 700 | this.updateBarHandle('vertical'); 701 | } 702 | if (this.horizontal) { 703 | this.initLayout('horizontal'); 704 | this.updateBarHandle('horizontal'); 705 | } 706 | } 707 | } 708 | 709 | unStyle() { 710 | if (this.horizontal) { 711 | this.$container.css({ 712 | height: '', 713 | 'padding-bottom': '' 714 | }); 715 | this.$content.css({ 716 | height: '' 717 | }); 718 | } 719 | if (this.vertical) { 720 | this.$container.css({ 721 | width: '', 722 | height: '', 723 | 'padding-right': '' 724 | }); 725 | this.$content.css({ 726 | width: '' 727 | }); 728 | } 729 | if (!this.options.containerSelector) { 730 | this.$wrap.css({ 731 | height: '' 732 | }); 733 | } 734 | } 735 | 736 | destroy() { 737 | this.$wrap.removeClass(`${this.classes.wrap}-vertical`) 738 | .removeClass(`${this.classes.wrap}-horizontal`) 739 | .removeClass(this.classes.wrap) 740 | .removeClass(this.options.enabledClass) 741 | .removeClass(this.classes.disabledClass); 742 | this.unStyle(); 743 | 744 | if (this.$bar) { 745 | this.$bar.remove(); 746 | } 747 | 748 | this.unbindEvents(); 749 | 750 | if (this.options.containerSelector) { 751 | this.$container.removeClass(this.classes.container); 752 | } else { 753 | this.$container.unwrap(); 754 | } 755 | if (!this.options.contentSelector) { 756 | this.$content.unwrap(); 757 | } 758 | this.$content.removeClass(this.classes.content); 759 | this.$element.data(NAMESPACE, null); 760 | this.trigger('destroy'); 761 | } 762 | 763 | static setDefaults(options) { 764 | $.extend(DEFAULTS, $.isPlainObject(options) && options); 765 | } 766 | } 767 | 768 | export default AsScrollable; 769 | -------------------------------------------------------------------------------- /src/defaults.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespace: 'asScrollable', 3 | 4 | skin: null, 5 | 6 | contentSelector: null, 7 | containerSelector: null, 8 | 9 | enabledClass: 'is-enabled', 10 | disabledClass: 'is-disabled', 11 | 12 | draggingClass: 'is-dragging', 13 | hoveringClass: 'is-hovering', 14 | scrollingClass: 'is-scrolling', 15 | 16 | direction: 'vertical', // vertical, horizontal, both, auto 17 | 18 | showOnHover: true, 19 | showOnBarHover: false, 20 | 21 | duration: 500, 22 | easing: 'ease-in', // linear, ease, ease-in, ease-out, ease-in-out 23 | 24 | responsive: true, 25 | throttle: 20, 26 | 27 | scrollbar: {} 28 | }; 29 | -------------------------------------------------------------------------------- /src/info.js: -------------------------------------------------------------------------------- 1 | export default { 2 | version:'0.4.9' 3 | }; 4 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import AsScrollable from './asScrollable'; 3 | import info from './info'; 4 | 5 | const NAMESPACE = 'asScrollable'; 6 | const OtherAsScrollable = $.fn.asScrollable; 7 | 8 | const jQueryAsScrollable = function(options, ...args) { 9 | if (typeof options === 'string') { 10 | let method = options; 11 | 12 | if (/^_/.test(method)) { 13 | return false; 14 | } else if ((/^(get)/.test(method))) { 15 | let instance = this.first().data(NAMESPACE); 16 | if (instance && typeof instance[method] === 'function') { 17 | return instance[method](...args); 18 | } 19 | } else { 20 | return this.each(function() { 21 | let instance = $.data(this, NAMESPACE); 22 | if (instance && typeof instance[method] === 'function') { 23 | instance[method](...args); 24 | } 25 | }); 26 | } 27 | } 28 | 29 | return this.each(function() { 30 | if (!$(this).data(NAMESPACE)) { 31 | $(this).data(NAMESPACE, new AsScrollable(this, options)); 32 | } 33 | }); 34 | }; 35 | 36 | $.fn.asScrollable = jQueryAsScrollable; 37 | 38 | $.asScrollable = $.extend({ 39 | setDefaults: AsScrollable.setDefaults, 40 | noConflict: function() { 41 | $.fn.asScrollable = OtherAsScrollable; 42 | return jQueryAsScrollable; 43 | } 44 | }, info); 45 | -------------------------------------------------------------------------------- /src/scss/asScrollable.scss: -------------------------------------------------------------------------------- 1 | $scrollable: asScrollable !default; 2 | $scrollable-bar-size: 4px !default; 3 | $scrollable-bar-dragging-size: 10px !default; 4 | $scrollable-bar-dragging-color: rgba(238, 238, 238, .6) !default; 5 | $scrollable-bar-hovering-color: rgba(238, 238, 238, .4) !default; 6 | $scrollable-bar-handle-offset: 5px !default; 7 | $scrollable-bar-handle-color: rgba(224, 224, 224, .6) !default; 8 | $scrollable-bar-handle-hovering-color: rgba(150, 150, 150, .6) !default; 9 | $scrollable-bar-handle-dragging-color: rgba(150, 150, 150, .8) !default; 10 | 11 | .#{$scrollable} { 12 | &.is-enabled { 13 | overflow: hidden !important; 14 | } 15 | 16 | &-container { 17 | .is-enabled & { 18 | box-sizing: content-box !important; 19 | overflow: hidden !important; 20 | } 21 | 22 | .is-enabled &::-webkit-scrollbar { 23 | width: 0; 24 | height: 0; 25 | -webkit-appearance: none; 26 | } 27 | 28 | .#{$scrollable}-vertical.is-enabled & { 29 | & { 30 | overflow-y: scroll !important; 31 | } 32 | } 33 | 34 | .#{$scrollable}-horizontal.is-enabled & { 35 | overflow-x: scroll !important; 36 | } 37 | } 38 | 39 | &-content { 40 | .is-enabled & { 41 | position: relative !important; 42 | overflow: visible !important; 43 | 44 | &::before, 45 | &::after { 46 | display: table; 47 | content: " "; 48 | } 49 | &::after { 50 | clear: both; 51 | } 52 | } 53 | } 54 | 55 | &-bar { 56 | .is-disabled & { 57 | display: none; 58 | } 59 | 60 | &-hide { 61 | opacity: 0; 62 | transition-delay: 400ms; 63 | } 64 | 65 | position: absolute; 66 | right: 0; 67 | bottom: 0; 68 | box-sizing: border-box; 69 | overflow: hidden; 70 | line-height: 0; 71 | user-select: none; 72 | border-radius: $scrollable-bar-size / 2; 73 | transition: opacity .5s; 74 | -webkit-touch-callout: none; 75 | user-input: disabled; 76 | user-focus: ignore; 77 | 78 | &.is-hovering { 79 | background: $scrollable-bar-hovering-color; 80 | } 81 | 82 | &.is-dragging { 83 | background: $scrollable-bar-dragging-color !important; 84 | opacity: 1; 85 | } 86 | 87 | &.is-disabled { 88 | display: none; 89 | } 90 | 91 | &-handle { 92 | position: absolute; 93 | top: 0; 94 | left: 0; 95 | line-height: 0; 96 | cursor: pointer; 97 | background: $scrollable-bar-handle-color; 98 | border-radius: $scrollable-bar-size / 2; 99 | transition: width, height .5s; 100 | } 101 | 102 | &.is-dragging &-handle { 103 | background: $scrollable-bar-handle-dragging-color !important; 104 | } 105 | 106 | &.is-dragging, 107 | &.is-hovering { 108 | border-radius: $scrollable-bar-dragging-size / 2; 109 | .#{$scrollable}-bar-handle { 110 | border-radius: $scrollable-bar-dragging-size / 2; 111 | } 112 | } 113 | 114 | &-vertical { 115 | width: $scrollable-bar-size; 116 | height: 100%; 117 | height: calc(100% - #{2 * $scrollable-bar-handle-offset}); 118 | margin: $scrollable-bar-handle-offset ($scrollable-bar-dragging-size - $scrollable-bar-size) / 2; 119 | } 120 | 121 | &-vertical { 122 | &.is-dragging, 123 | &.is-hovering { 124 | width: $scrollable-bar-dragging-size; 125 | margin: $scrollable-bar-handle-offset 1px; 126 | } 127 | } 128 | 129 | &-vertical &-handle { 130 | width: 100%; 131 | } 132 | 133 | &-horizontal { 134 | width: 100%; 135 | width: calc(100% - #{2 * $scrollable-bar-handle-offset}); 136 | height: $scrollable-bar-size; 137 | margin: ($scrollable-bar-dragging-size - $scrollable-bar-size) / 2 $scrollable-bar-handle-offset; 138 | } 139 | 140 | &-horizontal { 141 | &.is-dragging, 142 | &.is-hovering { 143 | height: $scrollable-bar-dragging-size; 144 | margin: 1px $scrollable-bar-handle-offset; 145 | } 146 | } 147 | 148 | &-horizontal &-handle { 149 | height: 100%; 150 | } 151 | } 152 | 153 | &.is-scrolling { 154 | .#{$scrollable}-bar { 155 | opacity: 1; 156 | transition: opacity 0; 157 | } 158 | } 159 | 160 | &.is-hovering { 161 | .#{$scrollable}-bar-handle { 162 | background: $scrollable-bar-handle-hovering-color; 163 | } 164 | } 165 | 166 | &.is-dragging { 167 | user-select: none; 168 | -webkit-touch-callout: none; 169 | user-input: disabled; 170 | user-focus: ignore; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | export function getTime() { 2 | if (typeof window.performance !== 'undefined' && window.performance.now) { 3 | return window.performance.now(); 4 | } 5 | return Date.now(); 6 | } 7 | 8 | export function isPercentage(n) { 9 | return typeof n === 'string' && n.indexOf('%') !== -1; 10 | } 11 | 12 | export function conventToPercentage(n) { 13 | if (n < 0) { 14 | n = 0; 15 | } else if (n > 1) { 16 | n = 1; 17 | } 18 | return `${parseFloat(n).toFixed(4) * 100}%`; 19 | } 20 | 21 | export function convertPercentageToFloat(n) { 22 | return parseFloat(n.slice(0, -1) / 100, 10); 23 | } 24 | 25 | export let isFFLionScrollbar = (() => { 26 | 'use strict'; 27 | 28 | let isOSXFF, ua, version; 29 | ua = window.navigator.userAgent; 30 | isOSXFF = /(?=.+Mac OS X)(?=.+Firefox)/.test(ua); 31 | if (!isOSXFF) { 32 | return false; 33 | } 34 | version = /Firefox\/\d{2}\./.exec(ua); 35 | if (version) { 36 | version = version[0].replace(/\D+/g, ''); 37 | } 38 | return isOSXFF && +version > 23; 39 | })(); 40 | --------------------------------------------------------------------------------