├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── app ├── index.html └── js │ ├── README.md │ └── skrollr.js ├── bower.json ├── dist ├── angular-skrollr.js └── angular-skrollr.min.js ├── package.json ├── scripts.json └── tests ├── e2e ├── app.js ├── protractor.conf.js ├── protractor.saucelabs.conf.js ├── scripts.html └── specs │ └── scenarios.js └── unit └── skrollr.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/components" 3 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/* 2 | !.gitkeep 3 | node_modules/ 4 | app/components/ 5 | docs/ 6 | css/ 7 | coverage/ 8 | tmp 9 | .DS_Store 10 | .idea 11 | .grunt 12 | *.log 13 | _SpecRunner.html 14 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : true, // true: Identifiers must be in camelCase 10 | "curly" : true, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 13 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 14 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "indent" : 4, // {int} Number of spaces to use for indentation 16 | "latedef" : true, // true: Require variables/functions to be defined before being used 17 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : true, // true: Prohibit use of empty blocks 20 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 21 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 22 | "plusplus" : false, // true: Prohibit use of `++` & `--` 23 | "quotmark" : "double", // Quotation mark consistency: 24 | // false : do nothing (default) 25 | // true : ensure whatever is used is consistent 26 | // "single" : require single quotes 27 | // "double" : require double quotes 28 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 29 | "unused" : false, // true: Require all defined variables be used 30 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 31 | "maxparams" : false, // {int} Max number of formal params allowed per function 32 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 33 | "maxstatements" : false, // {int} Max number statements per function 34 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 35 | "maxlen" : 200, // {int} Max number of characters per line 36 | 37 | // Relaxing 38 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 39 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 40 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 41 | "eqnull" : false, // true: Tolerate use of `== null` 42 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 43 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 44 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 45 | // (ex: `for each`, multiple try/catch, function expression…) 46 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 47 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 48 | "funcscope" : false, // true: Tolerate defining variables inside control statements 49 | "globalstrict" : true, // true: Allow global "use strict" (also enables 'strict') 50 | "iterator" : false, // true: Tolerate using the `__iterator__` property 51 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 52 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 53 | "laxcomma" : false, // true: Tolerate comma-first style coding 54 | "loopfunc" : false, // true: Tolerate functions being defined in loops 55 | "multistr" : true, // true: Tolerate multi-line strings 56 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 57 | "notypeof" : false, // true: Tolerate invalid typeof operator values 58 | "proto" : false, // true: Tolerate using the `__proto__` property 59 | "scripturl" : false, // true: Tolerate script-targeted URLs 60 | "shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 61 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 62 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 63 | "validthis" : false, // true: Tolerate using this in a non-constructor function 64 | 65 | // Environments 66 | "browser" : true, // Web Browser (window, document, etc) 67 | "browserify" : false, // Browserify (node.js code in the browser) 68 | "couch" : false, // CouchDB 69 | "devel" : true, // Development/debugging (alert, confirm, etc) 70 | "dojo" : false, // Dojo Toolkit 71 | "jasmine" : true, // Jasmine 72 | "jquery" : true, // jQuery 73 | "mocha" : false, // Mocha 74 | "mootools" : false, // MooTools 75 | "node" : false, // Node.js 76 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 77 | "prototypejs" : false, // Prototype and Scriptaculous 78 | "qunit" : false, // QUnit 79 | "rhino" : false, // Rhino 80 | "shelljs" : false, // ShellJS 81 | "worker" : false, // Web Workers 82 | "wsh" : false, // Windows Scripting Host 83 | "yui" : false, // Yahoo User Interface 84 | 85 | // Custom Globals 86 | "globals" : { // additional predefined global variables 87 | "angular" : true 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: 5 | - npm install -g bower grunt-cli 6 | - bower install 7 | install: 8 | - npm install 9 | script: 10 | - grunt test 11 | after_success: 12 | - ./node_modules/coveralls/bin/coveralls.js < coverage/lcov.info 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changes in 0.2.1 2 | 3 | * @lbehnke: Updated angular dependency version (#39) 4 | 5 | ## Changes in 0.2.0 6 | 7 | * @edwardoparearyee: Feature: add mobile detection (#36) 8 | 9 | ## Changes in 0.1.8 10 | 11 | * @edwardoparearyee: Fix: CI (no PR) 12 | 13 | ## Changes in 0.1.7 14 | 15 | * @edwardoparearyee: Fix: package.json (#34) 16 | 17 | ## Changes in 0.1.6 18 | 19 | * @edwardoparearyee: Feature: Publish to NPM (#32) 20 | * @Traksewt: add main to package.json (#31) 21 | 22 | ## Changes in 0.1.5 23 | 24 | * @edwardoparearyee: Feature: add support for angular 1.4 (#29) 25 | * @edwardoparearyee: Fix: Refresh skrollr on resize or scroll (#28) 26 | 27 | ## Changes in 0.1.4 28 | 29 | * @edwardoparearyee: Fix: Angular dependancies (#24) 30 | * @edwardoparearyee: Fix: Usage documentation (#23) 31 | 32 | ## Changes in 0.1.3 33 | 34 | * @edwardoparearyee: Fix: Deleted dist file 35 | 36 | 37 | ## Changes in 0.1.2 38 | 39 | * @edwardoparearyee: Improvement: Refresh single element (#20) 40 | 41 | ## Changes in 0.1.1 42 | 43 | * @edwardoparearyee: Fix: Correctly send argument to skrollr init (#18) 44 | 45 | ## Changes in 0.1.0 46 | 47 | * @edwardoparearyee: Feature: Initialise skrollr at run stage (#16) 48 | * @edwardoparearyee: Feature: Delay refresh (#15) 49 | 50 | ## Changes in 0.0.5 51 | 52 | * @edwardoparearyee: update documentation (no PR) 53 | * @jamesjwarren: Fix: update docs (#12) 54 | 55 | ## Changes in 0.0.4 56 | 57 | * @jamesjwarren: Fix: call destroy on skrollr instance (#8) 58 | 59 | ## Changes in 0.0.3 60 | 61 | * @jamesjwarren: Feature: destroy (#7) 62 | 63 | ## Changes in 0.0.2 64 | 65 | * @edwardoparearyee: Feature/fix travis deployment config (#6) 66 | * @jamesjwarren: Fix: skrollr refresh (#4) 67 | * @jamesjwarren: Feature: travis deploy (#3) 68 | 69 | ## Changes in 0.0.1 70 | 71 | * First Release 72 | 73 | 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | This is a quick guide on how to contribute to this project. Before you get started 4 | please have a read of the SOON_ frontend style guides. This will help keep everything 5 | consistent by following our coding practises and guidelines. 6 | - [LESS](app/less/README.md) 7 | - [JS](app/js/README.md) 8 | 9 | ## Getting Started 10 | 11 | - Make sure you have a [GitHub account](https://github.com/signup/free) 12 | - Create a new Github issue for your bug, feature or improvement, assuming 13 | one does not already exist. This is important as someone may already be working 14 | on this issue. 15 | - Explain the issue as clearly as possible including steps to reproduce and any 16 | error messages. 17 | - Or explain your reason for a new feature or improvement along with some sample 18 | code to back up your theory. 19 | - Clone the repo from github if you have push access to the thisissoon org or create 20 | a fork of the repo if you do not. 21 | 22 | ## Making Changes 23 | 24 | - Create a feature branch using git flow. 25 | - This branch should be branched of from develop. 26 | - Never work directly on the develop or master branches. 27 | - To quickly create a feature branch based on develop run: `git flow feature 28 | start `. 29 | - Make commits of logical units. 30 | - Check for unnecessary white-space with `git diff --check` before committing. 31 | - Check that you are not committing any code that is not relevant to the commit. 32 | - Check that you are not committing any code that breaks stable features and 33 | will be cause regression. 34 | - Make sure your commit messages are clear, specific and precise. 35 | - Make sure all code committed is following our coding guidelines. 36 | 37 | Example: 38 | 39 | ```` 40 | Adding blurb to CONTRIBUTING.md to provide clear contributing guidelines 41 | ```` 42 | __Important__ 43 | 44 | - Make sure you have included tests for new features. Or updated 45 | relevant tests if necessary when updating code. 46 | - Run all the tests to ensure nothing else was accidentally broken. 47 | - Make sure you have written adequate documentation for any new code. Or 48 | updated the relevant documentation if updating old code as specified by 49 | our coding practises. 50 | - Make sure you have prototyped any styling and mark-up in the `/modules/` 51 | directory (if working on any styling). 52 | - Your pull request may not be merged if any of the above hasn't been 53 | done. 54 | 55 | ## Submitting Changes 56 | 57 | - Make sure the pull request can be merged into develop successfully without conflicts 58 | by merging origin/develop into your feature branch. 59 | - Push your feature branch to github by pushing to origin. 60 | - Submit a pull request to the develop branch of the thisissoon repository on Github. 61 | - The SOON_ development team will look at your pull request and make comments on github 62 | as part of the pull request. 63 | - If your pull request follows all the correct guidelines and has been successfully 64 | reviewed by a member of the SOON_ development team it will be merged. 65 | - If the core team makes comments in your pull request, please make those changes and 66 | push them to github. Then comment in the pull request when you are happy these have been 67 | done. 68 | - If you have push access to the thisissoon make sure your pull requests has been reviewed by 69 | another member of the thisissoon team. 70 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var modRewrite = require("connect-modrewrite"); 4 | 5 | module.exports = function (grunt) { 6 | 7 | var base = grunt.option("baseDir") || "", 8 | protractorConf = grunt.option("ci") ? 9 | "tests/e2e/protractor.saucelabs.conf.js" : 10 | "tests/e2e/protractor.conf.js" ; 11 | 12 | grunt.initConfig({ 13 | 14 | pkg: grunt.file.readJSON("package.json"), 15 | 16 | config: { 17 | outputDir: "dist/", 18 | applicationFiles: grunt.file.readJSON("scripts.json").application, 19 | vendorFiles: grunt.file.readJSON("scripts.json").vendor 20 | }, 21 | 22 | connect: { 23 | options: { 24 | hostname: "0.0.0.0", 25 | port: 8000, 26 | base: base 27 | }, 28 | server: { 29 | options: { 30 | livereload: true, 31 | middleware: function ( connect, options, middlewares ) { 32 | var rules = (base === "dist") ? 33 | [ "^/[^\.]*$ /index.html" ] : 34 | [ "^/app/[^\.]*$ /app/index.html" ]; 35 | middlewares.unshift( modRewrite( rules ) ); 36 | return middlewares; 37 | } 38 | } 39 | }, 40 | servertest: { 41 | options: { 42 | keepalive: false, 43 | livereload: false, 44 | base: "dist", 45 | middleware: function ( connect, options, middlewares ) { 46 | var rules = [ "^/[^\.]*$ /index.html" ]; 47 | middlewares.unshift( modRewrite( rules ) ); 48 | return middlewares; 49 | } 50 | } 51 | } 52 | }, 53 | 54 | watch: { 55 | options: { 56 | nospawn: false, 57 | livereload: true 58 | }, 59 | css: { 60 | files: [ 61 | "app/index.html", 62 | 63 | "app/less/*.less", 64 | "app/less/**/*.less", 65 | "app/less/**/**/*.less", 66 | 67 | "app/partials/*.html", 68 | "app/partials/**/*.html", 69 | "app/partials/**/**/*.html", 70 | 71 | "modules/*.html", 72 | "modules/**/*.html", 73 | "modules/**/**/*.html" 74 | ], 75 | tasks: ["less:development"] 76 | }, 77 | javascript: { 78 | files: [ 79 | "app/js/*.js", 80 | "app/js/**/*.js", 81 | "app/js/**/**/*.js", 82 | 83 | "tests/unit/*.js", 84 | "tests/unit/**/*.js", 85 | "tests/unit/**/**/*.js" 86 | ], 87 | tasks: ["test:development"] 88 | } 89 | }, 90 | 91 | less: { 92 | options: { 93 | paths: ["app/less/"], 94 | cleancss: false 95 | }, 96 | development: { 97 | files: { "app/css/all.css": "app/less/main.less" }, 98 | options: { 99 | sourceMap: true, 100 | sourceMapFilename: "app/css/all.css.map", 101 | sourceMapURL: "all.css.map", 102 | outputSourceFiles: true 103 | } 104 | }, 105 | production: { 106 | files: { "<%= config.outputDir %>css/all.min.css": "app/less/main.less" }, 107 | options: { 108 | cleancss: true 109 | } 110 | } 111 | }, 112 | 113 | jshint: { 114 | options: { 115 | jshintrc: ".jshintrc" 116 | }, 117 | dist: { 118 | src: ["<%= config.applicationFiles %>"] 119 | } 120 | }, 121 | 122 | jasmine: { 123 | options: { 124 | vendor: ["<%= config.vendorFiles %>"], 125 | helpers:["app/components/angular-mocks/angular-mocks.js"], 126 | specs: ["tests/unit/**/*.js"], 127 | keepRunner: true, 128 | }, 129 | development: { 130 | src: ["<%= config.applicationFiles %>"], 131 | options: { 132 | template: require("grunt-template-jasmine-istanbul"), 133 | templateOptions: { 134 | coverage: "coverage/coverage.json", 135 | report: [ 136 | { 137 | type: "lcov", 138 | options: { 139 | dir: "coverage" 140 | } 141 | }, 142 | { 143 | type: "text-summary" 144 | } 145 | ] 146 | } 147 | } 148 | }, 149 | production: { 150 | src: ["<%= config.outputDir %><%= pkg.name %>.min.js"] 151 | } 152 | }, 153 | 154 | protractor: { 155 | options: { 156 | keepAlive: false, 157 | noColor: false 158 | }, 159 | dist: { 160 | options: { 161 | configFile: protractorConf 162 | } 163 | } 164 | }, 165 | 166 | protractor_webdriver: { 167 | dist: { 168 | options: { 169 | command: "webdriver-manager update && webdriver-manager start", 170 | } 171 | } 172 | }, 173 | 174 | concat: { 175 | options: { 176 | separator: ";", 177 | banner: "/*! <%= pkg.name %> - v<%= pkg.version %> - " + 178 | "<%= grunt.template.today(\"yyyy-mm-dd\") %> */\n" 179 | }, 180 | production: { 181 | src: [ "<%= config.applicationFiles %>" ], 182 | dest: "<%= config.outputDir %><%= pkg.name %>.js" 183 | } 184 | }, 185 | 186 | uglify: { 187 | options: { 188 | enclose: { window: "window" }, 189 | banner: "/*! <%= pkg.name %> - v<%= pkg.version %> - " + 190 | "<%= grunt.template.today(\"yyyy-mm-dd\") %> */\n" 191 | }, 192 | production: { 193 | files: { 194 | "<%= config.outputDir %><%= pkg.name %>.min.js": [ "<%= config.applicationFiles %>" ] 195 | } 196 | } 197 | }, 198 | 199 | copy: { 200 | images: { 201 | files: [{ 202 | expand: true, 203 | cwd: "app/img", 204 | src: ["**/*", "!test/**"], 205 | dest: "<%= config.outputDir %>img/" 206 | }] 207 | }, 208 | partials: { 209 | files: [{ 210 | expand: true, 211 | cwd: "app/partials", 212 | src: ["*.html"], 213 | dest: "<%= config.outputDir %>partials/" 214 | }] 215 | }, 216 | e2e: { 217 | files: [{ 218 | expand: true, 219 | flatten: true, 220 | src: [ 221 | "app/components/angular-mocks/angular-mocks.js", 222 | "tests/e2e/app.js" 223 | ], 224 | dest: "<%= config.outputDir %>e2e/" 225 | }] 226 | } 227 | }, 228 | 229 | clean: { 230 | beforeBuild: { 231 | src: ["<%= config.outputDir %>", "docs"] 232 | }, 233 | e2e: { 234 | src: ["<%= config.outputDir %>"] 235 | } 236 | }, 237 | 238 | processhtml: { 239 | options: { 240 | strip: true 241 | }, 242 | production: { 243 | files: { 244 | "<%= config.outputDir %>index.html": ["app/index.html"] 245 | } 246 | }, 247 | e2e: { 248 | files: { 249 | "<%= config.outputDir %>index.html": ["app/index.html"] 250 | } 251 | } 252 | }, 253 | 254 | yuidoc: { 255 | compile: { 256 | name: "<%= pkg.name %>", 257 | description: "<%= pkg.description %>", 258 | version: "<%= pkg.version %>", 259 | url: "<%= pkg.homepage %>", 260 | options: { 261 | paths: "app/js/", 262 | themedir: "node_modules/yuidoc-bootstrap-theme", 263 | helpers: ["node_modules/yuidoc-bootstrap-theme/helpers/helpers.js"], 264 | outdir: "docs/" 265 | } 266 | } 267 | }, 268 | 269 | bump: { 270 | options: { 271 | files: ["package.json", "bower.json"], 272 | updateConfigs: ["pkg"], 273 | commit: true, 274 | commitFiles: ["-a"], 275 | createTag: true, 276 | push: true, 277 | pushTo: "origin" 278 | } 279 | } 280 | 281 | }); 282 | 283 | grunt.loadNpmTasks("grunt-contrib-copy"); 284 | grunt.loadNpmTasks("grunt-contrib-clean"); 285 | grunt.loadNpmTasks("grunt-contrib-concat"); 286 | grunt.loadNpmTasks("grunt-contrib-uglify"); 287 | grunt.loadNpmTasks("grunt-contrib-less"); 288 | grunt.loadNpmTasks("grunt-contrib-watch"); 289 | grunt.loadNpmTasks("grunt-contrib-connect"); 290 | grunt.loadNpmTasks("grunt-contrib-jshint"); 291 | grunt.loadNpmTasks("grunt-contrib-yuidoc"); 292 | grunt.loadNpmTasks("grunt-contrib-jasmine"); 293 | grunt.loadNpmTasks("grunt-protractor-runner"); 294 | grunt.loadNpmTasks("grunt-protractor-webdriver"); 295 | grunt.loadNpmTasks("grunt-processhtml"); 296 | grunt.loadNpmTasks("grunt-bump"); 297 | 298 | grunt.registerTask("build", [ 299 | "clean:beforeBuild", 300 | "jshint", 301 | "concat", 302 | "uglify", 303 | "yuidoc" 304 | ]); 305 | 306 | grunt.registerTask("release", [ 307 | "bump-only", 308 | "build", 309 | "bump-commit" 310 | ]); 311 | 312 | grunt.registerTask("server", [ 313 | "less:development", 314 | "connect:server", 315 | "watch:css" 316 | ]); 317 | 318 | grunt.registerTask("serverjs", [ 319 | "less:development", 320 | "connect:server", 321 | "watch:javascript" 322 | ]); 323 | 324 | grunt.registerTask("serverall", [ 325 | "less:development", 326 | "connect:server", 327 | "watch" 328 | ]); 329 | 330 | grunt.registerTask("test:development", [ 331 | "jshint", 332 | "jasmine:development" 333 | ]); 334 | 335 | grunt.registerTask("test", [ 336 | "clean:beforeBuild", 337 | "jshint", 338 | "uglify", 339 | "jasmine:production" 340 | ]); 341 | 342 | grunt.registerTask("e2e", [ 343 | "uglify", 344 | "less:production", 345 | "copy", 346 | "processhtml:e2e", 347 | "connect:servertest", 348 | "protractor_webdriver", 349 | "protractor:dist", 350 | "clean:e2e" 351 | ]); 352 | 353 | grunt.registerTask("default", ["build"]); 354 | 355 | }; 356 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2014 Google, Inc. http://angularjs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Skrollr 2 | 3 | [![Build Status](https://travis-ci.org/thisissoon/angular-skrollr.svg?branch=master)](https://travis-ci.org/thisissoon/angular-skrollr) 4 | [![Coverage Status](https://coveralls.io/repos/thisissoon/angular-skrollr/badge.svg?branch=master)](https://coveralls.io/r/thisissoon/angular-skrollr?branch=master) 5 | 6 | Angular Skrollr wraps the skrollr.js library to provide a mechanisim for configuring, initialising skrollr and calling skrollr.refresh() when the DOM is updated. 7 | 8 | 9 | ## Install 10 | 11 | ### using Bower 12 | 13 | ``` 14 | bower install --save angular-skrollr 15 | ``` 16 | 17 | ### using NPM 18 | 19 | ``` 20 | npm install --save angular-skrollr 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```js 26 | // 1. configure skrollr in your app config 27 | var myApp = angular.module("myApp", ["sn.skrollr"]); 28 | myApp.config(["snSkrollrProvider", function(snSkrollrProvider) { 29 | snSkrollrProvider.config = { smoothScrolling: true, ... }; 30 | }]); 31 | 32 | // 2. initialise skrollr at runtime 33 | myApp.run(["snSkrollr", function(snSkrollr) { 34 | snSkrollr.init(); 35 | }]) 36 | 37 | ``` 38 | 39 | ### Disable on mobile 40 | ```js 41 | // 1. configure skrollr in your app config 42 | // and set disableMobile boolean to true 43 | var myApp = angular.module("myApp", ["sn.skrollr"]); 44 | myApp.config(["snSkrollrProvider", function(snSkrollrProvider) { 45 | snSkrollrProvider.config = { smoothScrolling: true, ... }; 46 | snSkrollrProvider.disableMobile = true; 47 | }]); 48 | 49 | ``` 50 | 51 | ```html 52 | 53 |
59 | ... 60 |
61 | ``` 62 | For more information on the skrollr configuration options that may be set in step one [check out this list](https://github.com/Prinzhorn/skrollr#skrollrinitoptions). 63 | 64 | --- 65 | 66 | This project structure is based on the [angular-seed](https://github.com/angular/angular-seed) application skeleton for a typical [AngularJS](http://angularjs.org/) web app. 67 | 68 | The project is preconfigured to install the Angular framework and a bunch of development and testing tools for instant web development gratification. 69 | 70 | 71 | ## Getting Started 72 | 73 | To get you started you can simply clone the repository and install the dependencies: 74 | 75 | ### Clone Angular Skrollr repository 76 | 77 | Clone the angular-skrollr repository using [git][git]: 78 | 79 | ``` 80 | cd path/to/parent/directory 81 | git clone git@github.com:thisissoon/angular-skrollr.git 82 | cd angular-skrollr 83 | ``` 84 | 85 | 86 | ### Install Dependencies 87 | 88 | We have two kinds of dependencies in this project: tools and angular framework code. The tools help us manage and test the application. 89 | 90 | * We get the tools we depend upon via `npm`, the [node package manager][npm]. 91 | * We get the angular code via `bower`, a [client-side code package manager][bower]. 92 | * We run regular tasks like code minification via `grunt`, a [javascript task runner][grunt]. 93 | 94 | 95 | The following tools require super user privileges so you will need to install them separately like so: 96 | 97 | ``` 98 | sudo npm install -g bower 99 | sudo npm install -g grunt-cli 100 | ``` 101 | 102 | We have preconfigured `npm` to automatically run `bower` so we can simply do: 103 | 104 | ``` 105 | npm install 106 | ``` 107 | 108 | Behind the scenes this will also call `bower install`. You should find that you have two new 109 | folders in your project. 110 | 111 | * `node_modules` - contains the npm packages for the tools we need 112 | * `app/components` - contains the angular framework files and other libraries 113 | 114 | ### Install Libraries 115 | 116 | We install our frontend libraries via `bower`, a [client-side code package manager][bower]. 117 | 118 | All frontend depenancies such as angular will be installed when running `npm install`. To manually install all dependencies run: 119 | 120 | ``` 121 | bower install 122 | ``` 123 | 124 | To install a new library such as bootstrap we can simply do: 125 | 126 | ``` 127 | bower install bootstrap --save 128 | ``` 129 | 130 | And this will download the bootstrap package from bower and also update the `bower.json` file to include that package. You will then need to add the script tag to `app/index.html` like so: 131 | 132 | ```html 133 | 134 | ``` 135 | 136 | ### Run the Application 137 | 138 | We have preconfigured the project with a simple development web server. The simplest way to start 139 | this server is: 140 | 141 | ``` 142 | grunt server 143 | ``` 144 | 145 | Now browse to the app at `http://localhost:8000/app/`. 146 | 147 | If you are doing any javascript development you can instead run: 148 | 149 | ``` 150 | grunt serverjs 151 | ``` 152 | 153 | To run tests as well every time a javascript file is updated 154 | 155 | To watch all files run: 156 | 157 | ``` 158 | grunt serverall 159 | ``` 160 | 161 | To run tests or compile less to css when the relevent files are updated. 162 | 163 | ### Running the build script 164 | 165 | To create a build to deploy for a production environment simply run: 166 | 167 | ``` 168 | grunt build 169 | ``` 170 | 171 | The build files will then be in the `dist/` directory. 172 | 173 | 174 | ## Directory Layout 175 | 176 | ``` 177 | 178 | app/ --> all of the files to be used in production 179 | components/ --> all of our javascript libraries (installed using bower) 180 | index.html --> app layout file (the main html template file of the app) 181 | js/ --> javascript files 182 | {app}/ --> angular module javascript files 183 | {app}.js --> angular module initialisation 184 | config.js --> angular module config 185 | controllers/ --> controllers 186 | {view}Ctrl.js 187 | directives/ --> directives 188 | {module}.js 189 | modules/ --> static html files for building and testing styling and mark up 190 | {module}/ 191 | index.html 192 | tests/ --> test config and source files 193 | e2e/ --> end-to-end specs 194 | specs/ 195 | scenarios.js 196 | protractor.conf.js --> config file for running e2e tests with Protractor 197 | unit/ --> unit level specs/tests 198 | {app}/ --> follows the same folder structure as javascript files in app folder 199 | controllers/ --> controller folder 200 | {view}Ctrl.js --> view controller tests 201 | directives/ 202 | {module}.js --> module directive test 203 | 204 | ``` 205 | 206 | ## Testing 207 | 208 | There are two kinds of tests in the angular-seed application: Unit tests and End to End tests. 209 | 210 | ### Running Unit Tests 211 | 212 | The angular-seed app comes preconfigured with unit tests. These are written in 213 | [Jasmine][jasmine], which we run with [Grunt][grunt]. 214 | 215 | * the configuration is found in `Gruntfile.js` 216 | * the unit tests are found in `tests/unit/`. 217 | 218 | The easiest way to run the unit tests is to do: 219 | 220 | ``` 221 | grunt test 222 | ``` 223 | 224 | This script will start the Jasmine test runner to execute the unit tests. You can also run: 225 | 226 | ``` 227 | grunt serverjs 228 | ``` 229 | 230 | Where the grunt watch command will sit and watch the source and test files for changes and then re-run the tests whenever any of them change. 231 | This is the recommended strategy; if you unit tests are being run every time you save a file then 232 | you receive instant feedback on any changes that break the expected code functionality. 233 | 234 | 235 | ### End to end testing 236 | 237 | The angular-seed app comes with end-to-end tests, again written in [Jasmine][jasmine]. These tests 238 | are run with the [Protractor][protractor] End-to-End test runner. It uses native events and has 239 | special features for Angular applications. 240 | 241 | * the configuration is found at `tests/e2e/protractor.conf.js` 242 | * the end-to-end tests are found in `tests/e2e/specs/` 243 | 244 | Protractor simulates interaction with our web app and verifies that the application responds 245 | correctly. Therefore, our web server needs to be serving up the application, so that Protractor 246 | can interact with it. To run end to end tests we first need to install protractor with global permissions. You may need to run this command with superuser privileges: 247 | 248 | ``` 249 | npm install -g protractor 250 | ``` 251 | 252 | Once you have ensured that the development web server hosting our application is up and running 253 | and WebDriver is updated, you can run the end-to-end tests using the supplied grunt task: 254 | 255 | ``` 256 | grunt e2e 257 | ``` 258 | 259 | Behind the scenes this will also run `webdriver-manager update && webdriver-manager start`. This will download and install the latest version of the stand-alone WebDriver tool and start the Selenium web server. This script will execute the end-to-end tests against the application being hosted on the 260 | development server. 261 | 262 | 263 | ## Contact 264 | 265 | For more information on AngularJS please check out http://angularjs.org/ 266 | 267 | [git]: http://git-scm.com/ 268 | [bower]: http://bower.io 269 | [npm]: https://www.npmjs.org/ 270 | [grunt]: http://gruntjs.com/ 271 | [node]: http://nodejs.org 272 | [protractor]: https://github.com/angular/protractor 273 | [jasmine]: https://jasmine.github.io/ 274 | [karma]: http://karma-runner.github.io 275 | [travis]: https://travis-ci.org/ 276 | [http-server]: https://github.com/nodeapps/http-server 277 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | SOON_ AngularJS Template 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/js/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Style Guide 2 | Welcome to the SOON_ JavaScript style guide. This style guide is mostly based on the javascript styleguide from [Airbnb](https://github.com/airbnb/javascript/blob/master/README.md). 3 | 4 | 5 | ## Table of Contents 6 | 7 | 1. [Types](#types) 8 | 1. [Objects](#objects) 9 | 1. [Arrays](#arrays) 10 | 1. [Strings](#strings) 11 | 1. [Functions](#functions) 12 | 1. [Properties](#properties) 13 | 1. [Variables](#variables) 14 | 1. [Hoisting](#hoisting) 15 | 1. [Conditional Expressions & Equality](#conditional-expressions--equality) 16 | 1. [Blocks](#blocks) 17 | 1. [Comments](#comments) 18 | 1. [Whitespace](#whitespace) 19 | 1. [Commas](#commas) 20 | 1. [Semicolons](#semicolons) 21 | 1. [Type Casting & Coercion](#type-casting--coercion) 22 | 1. [Naming Conventions](#naming-conventions) 23 | 1. [Accessors](#accessors) 24 | 1. [Constructors](#constructors) 25 | 1. [Events](#events) 26 | 1. [jQuery](#jquery) 27 | 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) 28 | 1. [Testing](#testing) 29 | 1. [Orginal source: The JavaScript Style Guide Guide](#the-javascript-style-guide-guide) 30 | 1. [License](#license) 31 | 32 | ## Types 33 | 34 | - **Primitives**: When you access a primitive type you work directly on its value 35 | 36 | + `string` 37 | + `number` 38 | + `boolean` 39 | + `null` 40 | + `undefined` 41 | 42 | ```javascript 43 | var foo = 1, 44 | bar = foo; 45 | 46 | bar = 9; 47 | 48 | console.log(foo, bar); // => 1, 9 49 | ``` 50 | - **Complex**: When you access a complex type you work on a reference to its value 51 | 52 | + `object` 53 | + `array` 54 | + `function` 55 | 56 | ```javascript 57 | var foo = [1, 2], 58 | bar = foo; 59 | 60 | bar[0] = 9; 61 | 62 | console.log(foo[0], bar[0]); // => 9, 9 63 | ``` 64 | 65 | **[⬆ back to top](#table-of-contents)** 66 | 67 | ## Objects 68 | 69 | - Use the literal syntax for object creation. 70 | 71 | ```javascript 72 | // bad 73 | var item = new Object(); 74 | 75 | // good 76 | var item = {}; 77 | ``` 78 | 79 | - Don't use [reserved words](http://es5.github.io/#x7.6.1) as keys. It won't work in IE8. [More info](https://github.com/airbnb/javascript/issues/61) 80 | 81 | ```javascript 82 | // bad 83 | var superman = { 84 | default: { clark: "kent" }, 85 | private: true 86 | }; 87 | 88 | // good 89 | var superman = { 90 | defaults: { clark: "kent" }, 91 | hidden: true 92 | }; 93 | ``` 94 | 95 | - Use readable synonyms in place of reserved words. 96 | 97 | ```javascript 98 | // bad 99 | var superman = { 100 | class: "alien" 101 | }; 102 | 103 | // bad 104 | var superman = { 105 | klass: "alien" 106 | }; 107 | 108 | // good 109 | var superman = { 110 | type: "alien" 111 | }; 112 | ``` 113 | 114 | **[⬆ back to top](#table-of-contents)** 115 | 116 | ## Arrays 117 | 118 | - Use the literal syntax for array creation 119 | 120 | ```javascript 121 | // bad 122 | var items = new Array(); 123 | 124 | // good 125 | var items = []; 126 | ``` 127 | 128 | - If you don't know array length use `Array.push()`. 129 | 130 | ```javascript 131 | var someStack = []; 132 | 133 | 134 | // bad 135 | someStack[someStack.length] = "abracadabra"; 136 | 137 | // good 138 | someStack.push("abracadabra"); 139 | ``` 140 | 141 | - When you need to copy an array use `Array.slice()`. [jsPerf](http://jsperf.com/converting-arguments-to-an-array/7) 142 | 143 | ```javascript 144 | var len = items.length, 145 | itemsCopy = [], 146 | i; 147 | 148 | // bad 149 | for (i = 0; i < len; i++) { 150 | itemsCopy[i] = items[i]; 151 | } 152 | 153 | // good 154 | itemsCopy = items.slice(); 155 | ``` 156 | 157 | - To convert an array-like object to an array, use `Array.slice()`. 158 | 159 | ```javascript 160 | function trigger() { 161 | var args = Array.slice(arguments); 162 | ... 163 | } 164 | ``` 165 | 166 | **[⬆ back to top](#table-of-contents)** 167 | 168 | 169 | ## Strings 170 | 171 | - Use double quotes `""` for strings 172 | 173 | ```javascript 174 | // bad 175 | var name = 'Bob Parr'; 176 | 177 | // good 178 | var name = "Bob Parr"; 179 | 180 | // bad 181 | var fullName = 'Bob ' + this.lastName; 182 | 183 | // good 184 | var fullName = "Bob " + this.lastName; 185 | ``` 186 | 187 | - Strings longer than 80 characters should be written across multiple lines using string concatenation. 188 | - Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40) 189 | 190 | ```javascript 191 | // bad 192 | var errorMessage = "This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast."; 193 | 194 | // bad 195 | var errorMessage = "This is a super long error that was thrown because \ 196 | of Batman. When you stop to think about how Batman had anything to do \ 197 | with this, you would get nowhere \ 198 | fast."; 199 | 200 | // good 201 | var errorMessage = "This is a super long error that was thrown because " + 202 | "of Batman. When you stop to think about how Batman had anything to do " + 203 | "with this, you would get nowhere fast."; 204 | ``` 205 | 206 | - When programmatically building up a string, use `Array.join()` instead of string concatenation. Mostly for IE: [jsPerf](http://jsperf.com/string-vs-array-concat/2). 207 | 208 | ```javascript 209 | var items, 210 | messages, 211 | length, 212 | i; 213 | 214 | messages = [{ 215 | state: "success", 216 | message: "This one worked." 217 | }, { 218 | state: "success", 219 | message: "This one worked as well." 220 | }, { 221 | state: "error", 222 | message: "This one did not work." 223 | }]; 224 | 225 | length = messages.length; 226 | 227 | // bad 228 | function inbox(messages) { 229 | items = ""; 236 | } 237 | 238 | // good 239 | function inbox(messages) { 240 | items = []; 241 | 242 | for (i = 0; i < length; i++) { 243 | items[i] = messages[i].message; 244 | } 245 | 246 | return ""; 247 | } 248 | ``` 249 | 250 | **[⬆ back to top](#table-of-contents)** 251 | 252 | 253 | ## Functions 254 | 255 | - Avoid declaring anonymous functions as it makes it harder to debug in a stack trace. 256 | - Always store functions in variables or objects. 257 | - Function expressions: 258 | 259 | ```javascript 260 | // bad: anonymous function expression 261 | var anonymous = function() { 262 | return true; 263 | }; 264 | 265 | // bad: not stored in variable or object 266 | function named() { 267 | return true; 268 | }; 269 | 270 | // good: named function expression 271 | var named = function named() { 272 | return true; 273 | }; 274 | 275 | // immediately-invoked function expression (IIFE) 276 | (function() { 277 | console.log("Welcome to the Internet. Please follow me."); 278 | })(); 279 | ``` 280 | 281 | - Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. 282 | - **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262"s note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). 283 | 284 | ```javascript 285 | // bad 286 | if (currentUser) { 287 | function test() { 288 | console.log("Nope."); 289 | } 290 | } 291 | 292 | // good 293 | var test; 294 | if (currentUser) { 295 | test = function test() { 296 | console.log("Yup."); 297 | }; 298 | } 299 | ``` 300 | 301 | - Never name a parameter `arguments`, this will take precedence over the `arguments` object that is given to every function scope. 302 | 303 | ```javascript 304 | // bad 305 | function nope(name, options, arguments) { 306 | // ...stuff... 307 | } 308 | 309 | // good 310 | function yup(name, options, args) { 311 | // ...stuff... 312 | } 313 | ``` 314 | 315 | **[⬆ back to top](#table-of-contents)** 316 | 317 | 318 | 319 | ## Properties 320 | 321 | - Use dot notation when accessing properties. 322 | 323 | ```javascript 324 | var luke = { 325 | jedi: true, 326 | age: 28 327 | }; 328 | 329 | // bad 330 | var isJedi = luke["jedi"]; 331 | 332 | // good 333 | var isJedi = luke.jedi; 334 | ``` 335 | 336 | - Use subscript notation `[]` when accessing properties with a variable. 337 | 338 | ```javascript 339 | var luke = { 340 | jedi: true, 341 | age: 28 342 | }; 343 | 344 | function getProp(prop) { 345 | return luke[prop]; 346 | } 347 | 348 | var isJedi = getProp("jedi"); 349 | ``` 350 | 351 | **[⬆ back to top](#table-of-contents)** 352 | 353 | 354 | ## Variables 355 | 356 | - Always use `var` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. 357 | 358 | ```javascript 359 | // bad 360 | superPower = new SuperPower(); 361 | 362 | // good 363 | var superPower = new SuperPower(); 364 | ``` 365 | 366 | - Use one `var` declaration for multiple variables and declare each variable on a newline. 367 | 368 | ```javascript 369 | // bad 370 | var items = getItems(); 371 | var goSportsTeam = true; 372 | var dragonball = "z"; 373 | 374 | // good 375 | var items = getItems(), 376 | goSportsTeam = true, 377 | dragonball = "z"; 378 | ``` 379 | 380 | - Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. 381 | 382 | ```javascript 383 | // bad 384 | var i, len, dragonball, 385 | items = getItems(), 386 | goSportsTeam = true; 387 | 388 | // bad 389 | var i, items = getItems(), 390 | dragonball, 391 | goSportsTeam = true, 392 | len; 393 | 394 | // good 395 | var items = getItems(), 396 | goSportsTeam = true, 397 | dragonball, 398 | length, 399 | i; 400 | ``` 401 | 402 | - Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues. 403 | 404 | ```javascript 405 | // bad 406 | function() { 407 | test(); 408 | console.log("doing stuff.."); 409 | 410 | //..other stuff.. 411 | 412 | var name = getName(); 413 | 414 | if (name === "test") { 415 | return false; 416 | } 417 | 418 | return name; 419 | } 420 | 421 | // good 422 | function() { 423 | var name = getName(); 424 | 425 | test(); 426 | console.log("doing stuff.."); 427 | 428 | //..other stuff.. 429 | 430 | if (name === "test") { 431 | return false; 432 | } 433 | 434 | return name; 435 | } 436 | 437 | // bad 438 | function() { 439 | var name = getName(); 440 | 441 | if (!arguments.length) { 442 | return false; 443 | } 444 | 445 | return true; 446 | } 447 | 448 | // good 449 | function() { 450 | if (!arguments.length) { 451 | return false; 452 | } 453 | 454 | var name = getName(); 455 | 456 | return true; 457 | } 458 | ``` 459 | 460 | **[⬆ back to top](#table-of-contents)** 461 | 462 | 463 | ## Hoisting 464 | 465 | - Variable declarations get hoisted to the top of their scope, their assignment does not. 466 | 467 | ```javascript 468 | // we know this wouldn't work (assuming there 469 | // is no notDefined global variable) 470 | function example() { 471 | console.log(notDefined); // => throws a ReferenceError 472 | } 473 | 474 | // creating a variable declaration after you 475 | // reference the variable will work due to 476 | // variable hoisting. Note: the assignment 477 | // value of `true` is not hoisted. 478 | function example() { 479 | console.log(declaredButNotAssigned); // => undefined 480 | var declaredButNotAssigned = true; 481 | } 482 | 483 | // The interpreter is hoisting the variable 484 | // declaration to the top of the scope. 485 | // Which means our example could be rewritten as: 486 | function example() { 487 | var declaredButNotAssigned; 488 | console.log(declaredButNotAssigned); // => undefined 489 | declaredButNotAssigned = true; 490 | } 491 | ``` 492 | 493 | - Anonymous function expressions hoist their variable name, but not the function assignment. 494 | 495 | ```javascript 496 | function example() { 497 | console.log(anonymous); // => undefined 498 | 499 | anonymous(); // => TypeError anonymous is not a function 500 | 501 | var anonymous = function() { 502 | console.log("anonymous function expression"); 503 | }; 504 | } 505 | ``` 506 | 507 | - Named function expressions hoist the variable name, not the function name or the function body. 508 | 509 | ```javascript 510 | function example() { 511 | console.log(named); // => undefined 512 | 513 | named(); // => TypeError named is not a function 514 | 515 | superPower(); // => ReferenceError superPower is not defined 516 | 517 | var named = function superPower() { 518 | console.log("Flying"); 519 | }; 520 | } 521 | 522 | // the same is true when the function name 523 | // is the same as the variable name. 524 | function example() { 525 | console.log(named); // => undefined 526 | 527 | named(); // => TypeError named is not a function 528 | 529 | var named = function named() { 530 | console.log("named"); 531 | } 532 | } 533 | ``` 534 | 535 | - Function declarations hoist their name and the function body. 536 | 537 | ```javascript 538 | function example() { 539 | superPower(); // => Flying 540 | 541 | function superPower() { 542 | console.log("Flying"); 543 | } 544 | } 545 | ``` 546 | 547 | - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/) 548 | 549 | **[⬆ back to top](#table-of-contents)** 550 | 551 | 552 | 553 | ## Conditional Expressions & Equality 554 | 555 | - Use `===` and `!==` over `==` and `!=`. 556 | - Conditional expressions are evaluated using coercion with the `ToBoolean` method and always follow these simple rules: 557 | 558 | + **Objects** evaluate to **true** 559 | + **Undefined** evaluates to **false** 560 | + **Null** evaluates to **false** 561 | + **Booleans** evaluate to **the value of the boolean** 562 | + **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true** 563 | + **Strings** evaluate to **false** if an empty string `""`, otherwise **true** 564 | 565 | ```javascript 566 | if ([0]) { 567 | // true 568 | // An array is an object, objects evaluate to true 569 | } 570 | ``` 571 | 572 | - Use shortcuts. 573 | 574 | ```javascript 575 | // bad 576 | if (name !== "") { 577 | // ...stuff... 578 | } 579 | 580 | // good 581 | if (name) { 582 | // ...stuff... 583 | } 584 | 585 | // bad 586 | if (collection.length > 0) { 587 | // ...stuff... 588 | } 589 | 590 | // good 591 | if (collection.length) { 592 | // ...stuff... 593 | } 594 | ``` 595 | 596 | - For more information see [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll 597 | 598 | **[⬆ back to top](#table-of-contents)** 599 | 600 | 601 | ## Blocks 602 | 603 | - Use braces with all multi-line blocks. 604 | 605 | ```javascript 606 | // bad 607 | if (test) 608 | return false; 609 | 610 | // good 611 | if (test) return false; 612 | 613 | // good 614 | if (test) { 615 | return false; 616 | } 617 | 618 | // bad 619 | function() { return false; } 620 | 621 | // good 622 | function() { 623 | return false; 624 | } 625 | ``` 626 | 627 | **[⬆ back to top](#table-of-contents)** 628 | 629 | 630 | ## Comments 631 | 632 | - Use `/** ... */` for multiline comments. Include a description, specify types and values for all parameters and return values. 633 | 634 | ```javascript 635 | // bad 636 | // make() returns a new element 637 | // based on the passed in tag name 638 | // 639 | // @param tag 640 | // @return element 641 | function make(tag) { 642 | 643 | // ...stuff... 644 | 645 | return element; 646 | } 647 | 648 | // good 649 | /** 650 | * make() returns a new element 651 | * based on the passed in tag name 652 | * 653 | * @param tag 654 | * @return element 655 | */ 656 | function make(tag) { 657 | 658 | // ...stuff... 659 | 660 | return element; 661 | } 662 | ``` 663 | 664 | - Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. 665 | 666 | ```javascript 667 | // bad 668 | var active = true; // is current tab 669 | 670 | // good 671 | // is current tab 672 | var active = true; 673 | 674 | // bad 675 | function getType() { 676 | console.log("fetching type..."); 677 | // set the default type to "no type" 678 | var type = this._type || "no type"; 679 | 680 | return type; 681 | } 682 | 683 | // good 684 | function getType() { 685 | console.log("fetching type..."); 686 | 687 | // set the default type to "no type" 688 | var type = this._type || "no type"; 689 | 690 | return type; 691 | } 692 | ``` 693 | 694 | - Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME -- need to figure this out` or `TODO -- need to implement`. 695 | 696 | - Use `// FIXME:` to annotate problems 697 | 698 | ```javascript 699 | function Calculator() { 700 | 701 | // FIXME: shouldn't use a global here 702 | total = 0; 703 | 704 | return this; 705 | } 706 | ``` 707 | 708 | - Use `// TODO:` to annotate solutions to problems 709 | 710 | ```javascript 711 | function Calculator() { 712 | 713 | // TODO: total should be configurable by an options param 714 | this.total = 0; 715 | 716 | return this; 717 | } 718 | ``` 719 | 720 | **[⬆ back to top](#table-of-contents)** 721 | 722 | 723 | ## Whitespace 724 | 725 | - Use soft tabs set to 4 spaces 726 | 727 | ```javascript 728 | // bad 729 | function() { 730 | ∙∙var name; 731 | } 732 | 733 | // bad 734 | function() { 735 | ∙var name; 736 | } 737 | 738 | // good 739 | function() { 740 | ∙∙∙∙var name; 741 | } 742 | ``` 743 | 744 | - Place 1 space before the leading brace. 745 | 746 | ```javascript 747 | // bad 748 | function test(){ 749 | console.log("test"); 750 | } 751 | 752 | // good 753 | function test() { 754 | console.log("test"); 755 | } 756 | 757 | // bad 758 | dog.set("attr",{ 759 | age: "1 year", 760 | breed: "Bernese Mountain Dog" 761 | }); 762 | 763 | // good 764 | dog.set("attr", { 765 | age: "1 year", 766 | breed: "Bernese Mountain Dog" 767 | }); 768 | ``` 769 | 770 | - Set off operators with spaces. 771 | 772 | ```javascript 773 | // bad 774 | var x=y+5; 775 | 776 | // good 777 | var x = y + 5; 778 | ``` 779 | 780 | - End files with a single newline character. 781 | 782 | ```javascript 783 | // bad 784 | (function(global) { 785 | // ...stuff... 786 | })(this); 787 | ``` 788 | 789 | ```javascript 790 | // bad 791 | (function(global) { 792 | // ...stuff... 793 | })(this);↵ 794 | ↵ 795 | ``` 796 | 797 | ```javascript 798 | // good 799 | (function(global) { 800 | // ...stuff... 801 | })(this);↵ 802 | ``` 803 | 804 | - Use indentation when making long method chains. 805 | 806 | ```javascript 807 | // bad 808 | $("#items").find(".selected").highlight().end().find(".open").updateCount(); 809 | 810 | // good 811 | $("#items") 812 | .find(".selected") 813 | .highlight() 814 | .end() 815 | .find(".open") 816 | .updateCount(); 817 | 818 | // bad 819 | var leds = stage.selectAll(".led").data(data).enter().append("svg:svg").class("led", true) 820 | .attr("width", (radius + margin) * 2).append("svg:g") 821 | .attr("transform", "translate(" + (radius + margin) + "," + (radius + margin) + ")") 822 | .call(tron.led); 823 | 824 | // good 825 | var leds = stage.selectAll(".led") 826 | .data(data) 827 | .enter().append("svg:svg") 828 | .class("led", true) 829 | .attr("width", (radius + margin) * 2) 830 | .append("svg:g") 831 | .attr("transform", "translate(" + (radius + margin) + "," + (radius + margin) + ")") 832 | .call(tron.led); 833 | ``` 834 | 835 | **[⬆ back to top](#table-of-contents)** 836 | 837 | ## Commas 838 | 839 | - Leading commas: **Nope.** 840 | 841 | ```javascript 842 | // bad 843 | var once 844 | , upon 845 | , aTime; 846 | 847 | // good 848 | var once, 849 | upon, 850 | aTime; 851 | 852 | // bad 853 | var hero = { 854 | firstName: "Bob" 855 | , lastName: "Parr" 856 | , heroName: "Mr. Incredible" 857 | , superPower: "strength" 858 | }; 859 | 860 | // good 861 | var hero = { 862 | firstName: "Bob", 863 | lastName: "Parr", 864 | heroName: "Mr. Incredible", 865 | superPower: "strength" 866 | }; 867 | ``` 868 | 869 | - Additional trailing comma: **Nope.** This can cause problems with IE6/7 and IE9 if it"s in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 ([source](http://es5.github.io/#D)): 870 | 871 | > Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this. 872 | 873 | ```javascript 874 | // bad 875 | var hero = { 876 | firstName: "Kevin", 877 | lastName: "Flynn", 878 | }; 879 | 880 | var heroes = [ 881 | "Batman", 882 | "Superman", 883 | ]; 884 | 885 | // good 886 | var hero = { 887 | firstName: "Kevin", 888 | lastName: "Flynn" 889 | }; 890 | 891 | var heroes = [ 892 | "Batman", 893 | "Superman" 894 | ]; 895 | ``` 896 | 897 | **[⬆ back to top](#table-of-contents)** 898 | 899 | 900 | ## Semicolons 901 | 902 | - **Yup.** 903 | 904 | ```javascript 905 | // bad 906 | (function() { 907 | var name = "Skywalker" 908 | return name 909 | })() 910 | 911 | // good 912 | (function() { 913 | var name = "Skywalker"; 914 | return name; 915 | })(); 916 | 917 | // good (guards against the function becoming an argument when two files with IIFEs are concatenated) 918 | ;(function() { 919 | var name = "Skywalker"; 920 | return name; 921 | })(); 922 | ``` 923 | 924 | [Read more](http://stackoverflow.com/a/7365214/1712802). 925 | 926 | **[⬆ back to top](#table-of-contents)** 927 | 928 | 929 | ## Type Casting & Coercion 930 | 931 | - Perform type coercion at the beginning of the statement. 932 | - Strings: 933 | 934 | ```javascript 935 | // => this.reviewScore = 9; 936 | 937 | // bad 938 | var totalScore = this.reviewScore + ""; 939 | 940 | // good 941 | var totalScore = "" + this.reviewScore; 942 | 943 | // bad 944 | var totalScore = "" + this.reviewScore + " total score"; 945 | 946 | // good 947 | var totalScore = this.reviewScore + " total score"; 948 | ``` 949 | 950 | - Use `parseInt` for Numbers and always with a radix for type casting. 951 | 952 | ```javascript 953 | var inputValue = "4"; 954 | 955 | // bad 956 | var val = new Number(inputValue); 957 | 958 | // bad 959 | var val = +inputValue; 960 | 961 | // bad 962 | var val = inputValue >> 0; 963 | 964 | // bad 965 | var val = parseInt(inputValue); 966 | 967 | // good 968 | var val = Number(inputValue); 969 | 970 | // good 971 | var val = parseInt(inputValue, 10); 972 | ``` 973 | 974 | - If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](http://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing. 975 | 976 | ```javascript 977 | // good 978 | /** 979 | * parseInt was the reason my code was slow. 980 | * Bitshifting the String to coerce it to a 981 | * Number made it a lot faster. 982 | */ 983 | var val = inputValue >> 0; 984 | ``` 985 | 986 | - **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](http://es5.github.io/#x4.3.19), but Bitshift operations always return a 32-bit integer ([source](http://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647: 987 | 988 | ```javascript 989 | 2147483647 >> 0 //=> 2147483647 990 | 2147483648 >> 0 //=> -2147483648 991 | 2147483649 >> 0 //=> -2147483647 992 | ``` 993 | 994 | - Booleans: 995 | 996 | ```javascript 997 | var age = 0; 998 | 999 | // bad 1000 | var hasAge = new Boolean(age); 1001 | 1002 | // good 1003 | var hasAge = Boolean(age); 1004 | 1005 | // good 1006 | var hasAge = !!age; 1007 | ``` 1008 | 1009 | **[⬆ back to top](#table-of-contents)** 1010 | 1011 | 1012 | ## Naming Conventions 1013 | 1014 | - Avoid single letter names. Be descriptive with your naming. 1015 | 1016 | ```javascript 1017 | // bad 1018 | function q() { 1019 | // ...stuff... 1020 | } 1021 | 1022 | // good 1023 | function query() { 1024 | // ..stuff.. 1025 | } 1026 | ``` 1027 | 1028 | - Use camelCase when naming objects, functions, and instances 1029 | 1030 | ```javascript 1031 | // bad 1032 | var OBJEcttsssss = {}; 1033 | var this_is_my_object = {}; 1034 | function c() {} 1035 | var u = new user({ 1036 | name: "Bob Parr" 1037 | }); 1038 | 1039 | // good 1040 | var thisIsMyObject = {}; 1041 | function thisIsMyFunction() {} 1042 | var user = new User({ 1043 | name: "Bob Parr" 1044 | }); 1045 | ``` 1046 | 1047 | - Use PascalCase when naming constructors or classes 1048 | 1049 | ```javascript 1050 | // bad 1051 | function user(options) { 1052 | this.name = options.name; 1053 | } 1054 | 1055 | var bad = new user({ 1056 | name: "nope" 1057 | }); 1058 | 1059 | // good 1060 | function User(options) { 1061 | this.name = options.name; 1062 | } 1063 | 1064 | var good = new User({ 1065 | name: "yup" 1066 | }); 1067 | ``` 1068 | 1069 | - Use a leading underscore `_` when naming private properties 1070 | 1071 | ```javascript 1072 | // bad 1073 | this.__firstName__ = "Panda"; 1074 | this.firstName_ = "Panda"; 1075 | 1076 | // good 1077 | this._firstName = "Panda"; 1078 | ``` 1079 | 1080 | - When saving a reference to `this` use `_this`. 1081 | 1082 | ```javascript 1083 | // bad 1084 | function() { 1085 | var self = this; 1086 | return function() { 1087 | console.log(self); 1088 | }; 1089 | } 1090 | 1091 | // bad 1092 | function() { 1093 | var that = this; 1094 | return function() { 1095 | console.log(that); 1096 | }; 1097 | } 1098 | 1099 | // good 1100 | function() { 1101 | var _this = this; 1102 | return function() { 1103 | console.log(_this); 1104 | }; 1105 | } 1106 | ``` 1107 | 1108 | - Name your functions. This is helpful for stack traces. 1109 | 1110 | ```javascript 1111 | // bad 1112 | var log = function(msg) { 1113 | console.log(msg); 1114 | }; 1115 | 1116 | // good 1117 | var log = function log(msg) { 1118 | console.log(msg); 1119 | }; 1120 | ``` 1121 | 1122 | - **Note:** IE8 and below exhibit some quirks with named function expressions. See [http://kangax.github.io/nfe/](http://kangax.github.io/nfe/) for more info. 1123 | 1124 | 1125 | **[⬆ back to top](#table-of-contents)** 1126 | 1127 | 1128 | ## Accessors 1129 | 1130 | - Accessor functions for properties are not required 1131 | - If you do make accessor functions use getVal() and setVal("hello") 1132 | 1133 | ```javascript 1134 | // bad 1135 | dragon.age(); 1136 | 1137 | // good 1138 | dragon.getAge(); 1139 | 1140 | // bad 1141 | dragon.age(25); 1142 | 1143 | // good 1144 | dragon.setAge(25); 1145 | ``` 1146 | 1147 | - If the property is a boolean, use isVal() or hasVal() 1148 | 1149 | ```javascript 1150 | // bad 1151 | if (!dragon.age()) { 1152 | return false; 1153 | } 1154 | 1155 | // good 1156 | if (!dragon.hasAge()) { 1157 | return false; 1158 | } 1159 | ``` 1160 | 1161 | - It"s okay to create get() and set() functions, but be consistent. 1162 | 1163 | ```javascript 1164 | function Jedi(options) { 1165 | options || (options = {}); 1166 | var lightsaber = options.lightsaber || "blue"; 1167 | this.set("lightsaber", lightsaber); 1168 | } 1169 | 1170 | Jedi.prototype.set = function(key, val) { 1171 | this[key] = val; 1172 | }; 1173 | 1174 | Jedi.prototype.get = function(key) { 1175 | return this[key]; 1176 | }; 1177 | ``` 1178 | 1179 | **[⬆ back to top](#table-of-contents)** 1180 | 1181 | 1182 | ## Constructors 1183 | 1184 | - Attach functions to variables or objects. This allows us to easily reference that function. 1185 | 1186 | ```javascript 1187 | // bad 1188 | function(msg) { 1189 | console.log(msg); 1190 | }; 1191 | 1192 | // good 1193 | obj.log = function log(msg) { 1194 | console.log(msg); 1195 | }; 1196 | 1197 | // good 1198 | var log = function log(msg) { 1199 | console.log(msg); 1200 | }; 1201 | ``` 1202 | 1203 | **[⬆ back to top](#table-of-contents)** 1204 | 1205 | 1206 | ## Events 1207 | 1208 | - When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: 1209 | 1210 | ```js 1211 | // bad 1212 | $(this).trigger("listingUpdated", listing.id); 1213 | 1214 | ... 1215 | 1216 | $(this).on("listingUpdated", function(e, listingId) { 1217 | // do something with listingId 1218 | }); 1219 | ``` 1220 | 1221 | prefer: 1222 | 1223 | ```js 1224 | // good 1225 | $(this).trigger("listingUpdated", { listingId : listing.id }); 1226 | 1227 | ... 1228 | 1229 | var eventHandler = function eventHandler(e, data) { 1230 | // do something with data.listingId 1231 | }); 1232 | 1233 | $(this).on("listingUpdated", eventHandler); 1234 | ``` 1235 | 1236 | - Always clear events during garbage collection. For example: 1237 | 1238 | ```js 1239 | 1240 | var eventHandler = function eventHandler(e, data) { 1241 | // do something with data.listingId 1242 | }); 1243 | 1244 | $(this).on("listingUpdated", eventHandler); 1245 | 1246 | ... 1247 | 1248 | var onDestroy = function onDestroy(){ 1249 | $(this).off("listingUpdated") 1250 | } 1251 | 1252 | ``` 1253 | 1254 | **[⬆ back to top](#table-of-contents)** 1255 | 1256 | 1257 | 1258 | ## jQuery 1259 | 1260 | - Prefix jQuery object variables with a `$`. 1261 | 1262 | ```javascript 1263 | // bad 1264 | var sidebar = $(".sidebar"); 1265 | 1266 | // good 1267 | var $sidebar = $(".sidebar"); 1268 | ``` 1269 | 1270 | - Cache jQuery lookups. 1271 | 1272 | ```javascript 1273 | // bad 1274 | function setSidebar() { 1275 | $(".sidebar").hide(); 1276 | 1277 | // ...stuff... 1278 | 1279 | $(".sidebar").css({ 1280 | "background-color": "pink" 1281 | }); 1282 | } 1283 | 1284 | // good 1285 | function setSidebar() { 1286 | var $sidebar = $(".sidebar"); 1287 | $sidebar.hide(); 1288 | 1289 | // ...stuff... 1290 | 1291 | $sidebar.css({ 1292 | "background-color": "pink" 1293 | }); 1294 | } 1295 | ``` 1296 | 1297 | - For DOM queries use Cascading `$(".sidebar ul")` or parent > child `$(".sidebar > ul")`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16) 1298 | - Use `find` with scoped jQuery object queries. 1299 | 1300 | ```javascript 1301 | // bad 1302 | $("ul", ".sidebar").hide(); 1303 | 1304 | // bad 1305 | $(".sidebar").find("ul").hide(); 1306 | 1307 | // good 1308 | $(".sidebar ul").hide(); 1309 | 1310 | // good 1311 | $(".sidebar > ul").hide(); 1312 | 1313 | // good 1314 | $sidebar.find("ul").hide(); 1315 | ``` 1316 | 1317 | **[⬆ back to top](#table-of-contents)** 1318 | 1319 | 1320 | ## ECMAScript 5 Compatibility 1321 | 1322 | - Refer to [Kangax](https://twitter.com/kangax/)"s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/) 1323 | 1324 | **[⬆ back to top](#table-of-contents)** 1325 | 1326 | 1327 | ## Testing 1328 | 1329 | - **Yup.** 1330 | 1331 | ```javascript 1332 | function() { 1333 | return true; 1334 | } 1335 | ``` 1336 | 1337 | **[⬆ back to top](#table-of-contents)** 1338 | 1339 | 1340 | ## Performance 1341 | 1342 | - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) 1343 | - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) 1344 | - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) 1345 | - [Bang Function](http://jsperf.com/bang-function) 1346 | - [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13) 1347 | - [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text) 1348 | - [Long String Concatenation](http://jsperf.com/ya-string-concat) 1349 | - Loading... 1350 | 1351 | **[⬆ back to top](#table-of-contents)** 1352 | 1353 | 1354 | ## Orginal source: The JavaScript Style Guide Guide 1355 | 1356 | - [Reference](https://github.com/airbnb/javascript/wiki/The-JavaScript-Style-Guide-Guide) 1357 | 1358 | 1359 | ## License 1360 | 1361 | (The MIT License) 1362 | 1363 | Copyright (c) 2014 Airbnb 1364 | 1365 | Permission is hereby granted, free of charge, to any person obtaining 1366 | a copy of this software and associated documentation files (the 1367 | "Software"), to deal in the Software without restriction, including 1368 | without limitation the rights to use, copy, modify, merge, publish, 1369 | distribute, sublicense, and/or sell copies of the Software, and to 1370 | permit persons to whom the Software is furnished to do so, subject to 1371 | the following conditions: 1372 | 1373 | The above copyright notice and this permission notice shall be 1374 | included in all copies or substantial portions of the Software. 1375 | 1376 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1377 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1378 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1379 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1380 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 1381 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1382 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1383 | 1384 | **[⬆ back to top](#table-of-contents)** 1385 | -------------------------------------------------------------------------------- /app/js/skrollr.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Wrap skrollr.js 4 | * @author SOON_ 5 | * @module sn.skrollr 6 | */ 7 | angular.module("sn.skrollr", []) 8 | 9 | /** 10 | * Provider to configuring skrollr 11 | * @example 12 | * snSkrollrProvider.config({ smoothScrolling: true }); 13 | * snSkrollr.init(); 14 | */ 15 | .provider("snSkrollr", function snSkrollrProvider() { 16 | 17 | var _this = this; 18 | 19 | /** 20 | * Skrollr initialisation options 21 | * @property {Object} config 22 | */ 23 | this.config = {}; 24 | 25 | /** 26 | * Instance of Skrollr 27 | * @property {Object} skrollrInstance 28 | */ 29 | this.skrollrInstance = {}; 30 | 31 | /** 32 | * Has the skrollInstance been initialised 33 | * @property {Boolean} hasBeenInitialised 34 | */ 35 | this.hasBeenInitialised = false; 36 | 37 | /** 38 | * Disable skrollr on mobile devices 39 | * @property {Boolean} disableMobile 40 | */ 41 | this.disableMobile = false; 42 | 43 | /** 44 | * Methods returned on snSkrollr service 45 | * @property {Object} serviceMethods 46 | */ 47 | this.serviceMethods = {}; 48 | 49 | /** 50 | * snSkroller service 51 | */ 52 | this.$get = [ 53 | "$window", 54 | "$document", 55 | "$rootScope", 56 | /** 57 | * @constructor 58 | * @param {Object} $window angular wrapper for window 59 | * @param {Object} $document angular wrapper for document 60 | * @param {Object} $rootScope angular root application scope 61 | */ 62 | function($window, $document, $rootScope) { 63 | 64 | _this.serviceMethods = { 65 | 66 | /** 67 | * Initialise skrollrjs with config options 68 | * @method init 69 | */ 70 | init: function(config) { 71 | 72 | if (_this.disableMobile && _this.serviceMethods.isMobile.any()) { 73 | return; 74 | } 75 | 76 | var skrollrConfig = config ? config : _this.config, 77 | skrollrInit = function skrollrInit(){ 78 | _this.skrollrInstance = $window.skrollr.init(skrollrConfig); 79 | _this.hasBeenInitialised = true; 80 | _this.serviceMethods.refresh(); 81 | }; 82 | 83 | $document.ready(function () { 84 | if (!$rootScope.$$phase) { 85 | $rootScope.$apply(skrollrInit); 86 | } else { 87 | skrollrInit(); 88 | } 89 | }); 90 | 91 | }, 92 | /** 93 | * http://www.abeautifulsite.net/detecting-mobile-devices-with-javascript/ 94 | * @property {Object} isMobile 95 | */ 96 | isMobile: { 97 | Android: function() { 98 | return $window.navigator.userAgent.match(/Android/i); 99 | }, 100 | iOS: function() { 101 | return $window.navigator.userAgent.match(/iPhone|iPad|iPod/i); 102 | }, 103 | BlackBerry: function() { 104 | return $window.navigator.userAgent.match(/BlackBerry/i); 105 | }, 106 | Opera: function() { 107 | return $window.navigator.userAgent.match(/Opera Mini/i); 108 | }, 109 | Windows: function() { 110 | return $window.navigator.userAgent.match(/IEMobile/i); 111 | }, 112 | any: function() { 113 | return ( _this.serviceMethods.isMobile.Android() || 114 | _this.serviceMethods.isMobile.iOS() || 115 | _this.serviceMethods.isMobile.BlackBerry() || 116 | _this.serviceMethods.isMobile.Opera() || 117 | _this.serviceMethods.isMobile.Windows() ); 118 | } 119 | }, 120 | 121 | /** 122 | * Call refresh on Skrollr instance 123 | * Useful for resetting skrollr after modifying the DOM 124 | * @method refresh 125 | */ 126 | refresh: function($element) { 127 | if (_this.hasBeenInitialised) { 128 | _this.skrollrInstance.refresh($element); 129 | } 130 | }, 131 | 132 | /** 133 | * Call skrollr.destroy() 134 | * @method refresh 135 | */ 136 | destroy: function() { 137 | if (_this.hasBeenInitialised) { 138 | _this.skrollrInstance.destroy(); 139 | _this.hasBeenInitialised = false; 140 | } 141 | } 142 | }; 143 | 144 | return _this.serviceMethods; 145 | } 146 | ]; 147 | }) 148 | 149 | /** 150 | * Refresh skrollrjs on element init 151 | * @class snSkrollr 152 | */ 153 | .directive("snSkrollr", [ 154 | "$timeout", 155 | "$window", 156 | "snSkrollr", 157 | /** 158 | * @constructor 159 | */ 160 | function ($timeout, $window, snSkrollr){ 161 | return { 162 | restrict: "AE", 163 | link: function($scope, $element) { 164 | 165 | /** 166 | * delay refresh to allow time for 167 | * template to render 168 | * @property timer 169 | */ 170 | $scope.timer = $timeout(function(){ 171 | snSkrollr.refresh($element); 172 | }, 100); 173 | 174 | /** 175 | * Event handler for scroll and resize. Cancel timer if there 176 | * is an active one and then create a new timer to replace. 177 | * This is so we can wait until the user has finished scrolling 178 | * before calling refresh. This helps with performance. 179 | * @method onChange 180 | */ 181 | $scope.onChange = function onChange(){ 182 | if ($scope.timer) { 183 | $timeout.cancel($scope.timer); 184 | } 185 | 186 | $scope.timer = $timeout(function(){ 187 | snSkrollr.refresh($element); 188 | }, 200); 189 | }; 190 | 191 | angular.element($window).on("scroll", $scope.onChange); 192 | angular.element($window).on("resize", $scope.onChange); 193 | 194 | } 195 | }; 196 | } 197 | ]); 198 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-skrollr", 3 | "description": "Angular Skrollr wraps the skrollr.js library to provide a mechanisim for calling skrollr.refresh() when the DOM is updated", 4 | "version": "0.2.1", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/thisissoon/angular-skrollr" 8 | }, 9 | "license": "MIT", 10 | "main": "dist/angular-skrollr.js", 11 | "ignore": [ 12 | "app", 13 | "tests", 14 | ".bowerrc", 15 | ".editorconfig", 16 | ".gitignore", 17 | ".jshintrc", 18 | ".travis.yml", 19 | "Gruntfile.js", 20 | "package.json", 21 | "scripts.json" 22 | ], 23 | "private": false, 24 | "dependencies": { 25 | "angular": ">= 1.2 < 1.6", 26 | "skrollr": "0.6.30" 27 | }, 28 | "devDependencies": { 29 | "angular-mocks": ">= 1.2 < 1.6" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dist/angular-skrollr.js: -------------------------------------------------------------------------------- 1 | /*! angular-skrollr - v0.2.1 - 2016-09-20 */ 2 | "use strict"; 3 | /** 4 | * Wrap skrollr.js 5 | * @author SOON_ 6 | * @module sn.skrollr 7 | */ 8 | angular.module("sn.skrollr", []) 9 | 10 | /** 11 | * Provider to configuring skrollr 12 | * @example 13 | * snSkrollrProvider.config({ smoothScrolling: true }); 14 | * snSkrollr.init(); 15 | */ 16 | .provider("snSkrollr", function snSkrollrProvider() { 17 | 18 | var _this = this; 19 | 20 | /** 21 | * Skrollr initialisation options 22 | * @property {Object} config 23 | */ 24 | this.config = {}; 25 | 26 | /** 27 | * Instance of Skrollr 28 | * @property {Object} skrollrInstance 29 | */ 30 | this.skrollrInstance = {}; 31 | 32 | /** 33 | * Has the skrollInstance been initialised 34 | * @property {Boolean} hasBeenInitialised 35 | */ 36 | this.hasBeenInitialised = false; 37 | 38 | /** 39 | * Disable skrollr on mobile devices 40 | * @property {Boolean} disableMobile 41 | */ 42 | this.disableMobile = false; 43 | 44 | /** 45 | * Methods returned on snSkrollr service 46 | * @property {Object} serviceMethods 47 | */ 48 | this.serviceMethods = {}; 49 | 50 | /** 51 | * snSkroller service 52 | */ 53 | this.$get = [ 54 | "$window", 55 | "$document", 56 | "$rootScope", 57 | /** 58 | * @constructor 59 | * @param {Object} $window angular wrapper for window 60 | * @param {Object} $document angular wrapper for document 61 | * @param {Object} $rootScope angular root application scope 62 | */ 63 | function($window, $document, $rootScope) { 64 | 65 | _this.serviceMethods = { 66 | 67 | /** 68 | * Initialise skrollrjs with config options 69 | * @method init 70 | */ 71 | init: function(config) { 72 | 73 | if (_this.disableMobile && _this.serviceMethods.isMobile.any()) { 74 | return; 75 | } 76 | 77 | var skrollrConfig = config ? config : _this.config, 78 | skrollrInit = function skrollrInit(){ 79 | _this.skrollrInstance = $window.skrollr.init(skrollrConfig); 80 | _this.hasBeenInitialised = true; 81 | _this.serviceMethods.refresh(); 82 | }; 83 | 84 | $document.ready(function () { 85 | if (!$rootScope.$$phase) { 86 | $rootScope.$apply(skrollrInit); 87 | } else { 88 | skrollrInit(); 89 | } 90 | }); 91 | 92 | }, 93 | /** 94 | * http://www.abeautifulsite.net/detecting-mobile-devices-with-javascript/ 95 | * @property {Object} isMobile 96 | */ 97 | isMobile: { 98 | Android: function() { 99 | return $window.navigator.userAgent.match(/Android/i); 100 | }, 101 | iOS: function() { 102 | return $window.navigator.userAgent.match(/iPhone|iPad|iPod/i); 103 | }, 104 | BlackBerry: function() { 105 | return $window.navigator.userAgent.match(/BlackBerry/i); 106 | }, 107 | Opera: function() { 108 | return $window.navigator.userAgent.match(/Opera Mini/i); 109 | }, 110 | Windows: function() { 111 | return $window.navigator.userAgent.match(/IEMobile/i); 112 | }, 113 | any: function() { 114 | return ( _this.serviceMethods.isMobile.Android() || 115 | _this.serviceMethods.isMobile.iOS() || 116 | _this.serviceMethods.isMobile.BlackBerry() || 117 | _this.serviceMethods.isMobile.Opera() || 118 | _this.serviceMethods.isMobile.Windows() ); 119 | } 120 | }, 121 | 122 | /** 123 | * Call refresh on Skrollr instance 124 | * Useful for resetting skrollr after modifying the DOM 125 | * @method refresh 126 | */ 127 | refresh: function($element) { 128 | if (_this.hasBeenInitialised) { 129 | _this.skrollrInstance.refresh($element); 130 | } 131 | }, 132 | 133 | /** 134 | * Call skrollr.destroy() 135 | * @method refresh 136 | */ 137 | destroy: function() { 138 | if (_this.hasBeenInitialised) { 139 | _this.skrollrInstance.destroy(); 140 | _this.hasBeenInitialised = false; 141 | } 142 | } 143 | }; 144 | 145 | return _this.serviceMethods; 146 | } 147 | ]; 148 | }) 149 | 150 | /** 151 | * Refresh skrollrjs on element init 152 | * @class snSkrollr 153 | */ 154 | .directive("snSkrollr", [ 155 | "$timeout", 156 | "$window", 157 | "snSkrollr", 158 | /** 159 | * @constructor 160 | */ 161 | function ($timeout, $window, snSkrollr){ 162 | return { 163 | restrict: "AE", 164 | link: function($scope, $element) { 165 | 166 | /** 167 | * delay refresh to allow time for 168 | * template to render 169 | * @property timer 170 | */ 171 | $scope.timer = $timeout(function(){ 172 | snSkrollr.refresh($element); 173 | }, 100); 174 | 175 | /** 176 | * Event handler for scroll and resize. Cancel timer if there 177 | * is an active one and then create a new timer to replace. 178 | * This is so we can wait until the user has finished scrolling 179 | * before calling refresh. This helps with performance. 180 | * @method onChange 181 | */ 182 | $scope.onChange = function onChange(){ 183 | if ($scope.timer) { 184 | $timeout.cancel($scope.timer); 185 | } 186 | 187 | $scope.timer = $timeout(function(){ 188 | snSkrollr.refresh($element); 189 | }, 200); 190 | }; 191 | 192 | angular.element($window).on("scroll", $scope.onChange); 193 | angular.element($window).on("resize", $scope.onChange); 194 | 195 | } 196 | }; 197 | } 198 | ]); 199 | -------------------------------------------------------------------------------- /dist/angular-skrollr.min.js: -------------------------------------------------------------------------------- 1 | /*! angular-skrollr - v0.2.1 - 2016-09-20 */ 2 | !function(a){"use strict";angular.module("sn.skrollr",[]).provider("snSkrollr",function(){var a=this;this.config={},this.skrollrInstance={},this.hasBeenInitialised=!1,this.disableMobile=!1,this.serviceMethods={},this.$get=["$window","$document","$rootScope",function(b,c,d){return a.serviceMethods={init:function(e){if(!a.disableMobile||!a.serviceMethods.isMobile.any()){var f=e?e:a.config,g=function(){a.skrollrInstance=b.skrollr.init(f),a.hasBeenInitialised=!0,a.serviceMethods.refresh()};c.ready(function(){d.$$phase?g():d.$apply(g)})}},isMobile:{Android:function(){return b.navigator.userAgent.match(/Android/i)},iOS:function(){return b.navigator.userAgent.match(/iPhone|iPad|iPod/i)},BlackBerry:function(){return b.navigator.userAgent.match(/BlackBerry/i)},Opera:function(){return b.navigator.userAgent.match(/Opera Mini/i)},Windows:function(){return b.navigator.userAgent.match(/IEMobile/i)},any:function(){return a.serviceMethods.isMobile.Android()||a.serviceMethods.isMobile.iOS()||a.serviceMethods.isMobile.BlackBerry()||a.serviceMethods.isMobile.Opera()||a.serviceMethods.isMobile.Windows()}},refresh:function(b){a.hasBeenInitialised&&a.skrollrInstance.refresh(b)},destroy:function(){a.hasBeenInitialised&&(a.skrollrInstance.destroy(),a.hasBeenInitialised=!1)}},a.serviceMethods}]}).directive("snSkrollr",["$timeout","$window","snSkrollr",function(a,b,c){return{restrict:"AE",link:function(d,e){d.timer=a(function(){c.refresh(e)},100),d.onChange=function(){d.timer&&a.cancel(d.timer),d.timer=a(function(){c.refresh(e)},200)},angular.element(b).on("scroll",d.onChange),angular.element(b).on("resize",d.onChange)}}}])}(window); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-skrollr", 3 | "private": false, 4 | "version": "0.2.1", 5 | "description": "Angular Skrollr wraps the skrollr.js library to provide a mechanisim for configuring, initialising skrollr and calling skrollr.refresh() when the DOM is updated", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/thisissoon/angular-skrollr" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/thisissoon/angular-skrollr/issues", 12 | "email": "dorks@thisissoon.com" 13 | }, 14 | "license": "MIT", 15 | "author": "SOON_ ", 16 | "contributors": [ 17 | { "name": "Ed Opare-Aryee", "email": "ed@thisissoon.com" }, 18 | { "name": "James Warren", "email": "james@thisissoon.com" }, 19 | { "name": "Kenny Sabir", "email": "kenny.sabir@agriwebb.com" }, 20 | { "name": "Lars Behnke", "email": "lars.behnke@gmx.net" } 21 | ], 22 | "main": "dist/angular-skrollr.js", 23 | "keywords": [ 24 | "angular", 25 | "skrollr", 26 | "parallax", 27 | "directive" 28 | ], 29 | "files": [ 30 | "app/js", 31 | "dist", 32 | "node_modules", 33 | "CHANGELOG.md", 34 | "README.md", 35 | "LICENSE" 36 | ], 37 | "dependencies": { 38 | "angular": ">= 1.2 < 1.9", 39 | "skrollr": "~0.6.26" 40 | }, 41 | "devDependencies": { 42 | "angular-mocks": ">= 1.2 < 1.6", 43 | "bower": "~1.8.8", 44 | "connect-livereload": "~0.5.4", 45 | "connect-modrewrite": "~0.9.0", 46 | "coveralls": "~2.11.9", 47 | "grunt": "~0.4.5", 48 | "grunt-bump": "~0.7.0", 49 | "grunt-contrib-clean": "~1.0.0", 50 | "grunt-contrib-concat": "~1.0.0", 51 | "grunt-contrib-connect": "~1.0.1", 52 | "grunt-contrib-copy": "~1.0.0", 53 | "grunt-contrib-jasmine": "~1.0.0", 54 | "grunt-contrib-jshint": "~1.0.0", 55 | "grunt-contrib-less": "~1.2.0", 56 | "grunt-contrib-uglify": "~1.0.1", 57 | "grunt-contrib-watch": "~1.0.0", 58 | "grunt-contrib-yuidoc": "~1.0.0", 59 | "grunt-htmllint": "~0.2.7", 60 | "grunt-lesslint": "~3.1.0", 61 | "grunt-ng-constant": "~2.0.1", 62 | "grunt-processhtml": "~0.3.13", 63 | "grunt-protractor-runner": "~2.1.0", 64 | "grunt-protractor-webdriver": "~0.2.5", 65 | "grunt-sails-linker": "~0.10.1", 66 | "grunt-template-jasmine-istanbul": "~0.4.0", 67 | "protractor": "~2.5.1", 68 | "yuidoc-bootstrap-theme": "~1.0.6" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /scripts.json: -------------------------------------------------------------------------------- 1 | { 2 | "vendor": [ 3 | "app/components/angular/angular.js" 4 | ], 5 | "application": [ 6 | "app/js/skrollr.js" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /tests/e2e/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * This module runs e2e test by setting up a module to make our 4 | * backend assertions e.g. mock the responses from our api before 5 | * lauching our actual application. 6 | * @main sn.skrollr.e2e 7 | * @module sn.skrollr.e2e 8 | * @author SOON_ 9 | */ 10 | angular.module("sn.skrollr.e2e", ["sn.skrollr", "ngMockE2E"]) 11 | .run([ 12 | "$httpBackend", 13 | function ($httpBackend) { 14 | 15 | $httpBackend.whenGET(/partials\/.*/).passThrough(); 16 | 17 | } 18 | ]); 19 | -------------------------------------------------------------------------------- /tests/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // A reference configuration file. 2 | exports.config = { 3 | // ----- How to setup Selenium ----- 4 | // 5 | // There are three ways to specify how to use Selenium. Specify one of the 6 | // following: 7 | // 8 | // 1. seleniumServerJar - to start Selenium Standalone locally. 9 | // 2. seleniumAddress - to connect to a Selenium server which is already 10 | // running. 11 | // 3. sauceUser/sauceKey - to use remote Selenium servers via SauceLabs. 12 | // 13 | // If the chromeOnly option is specified, no Selenium server will be started, 14 | // and chromeDriver will be used directly (from the location specified in 15 | // chromeDriver) 16 | 17 | // The location of the selenium standalone server .jar file, relative 18 | // to the location of this config. If no other method of starting selenium 19 | // is found, this will default to 20 | // node_modules/protractor/selenium/selenium-server... 21 | seleniumServerJar: null, 22 | // The port to start the selenium server on, or null if the server should 23 | // find its own unused port. 24 | seleniumPort: null, 25 | // Chromedriver location is used to help the selenium standalone server 26 | // find chromedriver. This will be passed to the selenium jar as 27 | // the system property webdriver.chrome.driver. If null, selenium will 28 | // attempt to find chromedriver using PATH. 29 | chromeDriver: './selenium/chromedriver', 30 | // If true, only chromedriver will be started, not a standalone selenium. 31 | // Tests for browsers other than chrome will not run. 32 | chromeOnly: false, 33 | // Additional command line options to pass to selenium. For example, 34 | // if you need to change the browser timeout, use 35 | // seleniumArgs: ['-browserTimeout=60'], 36 | seleniumArgs: [], 37 | 38 | // If sauceUser and sauceKey are specified, seleniumServerJar will be ignored. 39 | // The tests will be run remotely using SauceLabs. 40 | sauceUser: null, 41 | sauceKey: null, 42 | 43 | // The address of a running selenium server. If specified, Protractor will 44 | // connect to an already running instance of selenium. This usually looks like 45 | // seleniumAddress: 'http://localhost:4444/wd/hub' 46 | seleniumAddress: "http://localhost:4444/wd/hub", 47 | 48 | // The timeout for each script run on the browser. This should be longer 49 | // than the maximum time your application needs to stabilize between tasks. 50 | allScriptsTimeout: 11000, 51 | 52 | // ----- What tests to run ----- 53 | // 54 | // Spec patterns are relative to the location of this config. 55 | specs: [ 56 | 'specs/*.js', 57 | ], 58 | 59 | // ----- Capabilities to be passed to the webdriver instance ---- 60 | // 61 | // For a full list of available capabilities, see 62 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities 63 | // and 64 | // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js 65 | // capabilities: { 66 | // 'browserName': 'chrome' 67 | // }, 68 | // 69 | // install safari driver here: 70 | // https://code.google.com/p/selenium/issues/detail?id=7933#c33 71 | 72 | multiCapabilities: [{ 73 | 'browserName': 'firefox' 74 | }, { 75 | 'browserName': 'chrome' 76 | // }, { 77 | // 'browserName': 'safari' 78 | // }, { 79 | // 'browserName': 'internet explorer' 80 | }], 81 | 82 | // ----- More information for your tests ---- 83 | // 84 | // A base URL for your application under test. Calls to protractor.get() 85 | // with relative paths will be prepended with this. 86 | baseUrl: 'http://localhost:8000', 87 | 88 | // Selector for the element housing the angular app - this defaults to 89 | // body, but is necessary if ng-app is on a descendant of 90 | rootElement: 'body', 91 | 92 | // A callback function called once protractor is ready and available, and 93 | // before the specs are executed 94 | // You can specify a file containing code to run by setting onPrepare to 95 | // the filename string. 96 | onPrepare: function() { 97 | // At this point, global 'protractor' object will be set up, and jasmine 98 | // will be available. For example, you can add a Jasmine reporter with: 99 | // jasmine.getEnv().addReporter(new jasmine.JUnitXmlReporter( 100 | // 'outputdir/', true, true)); 101 | }, 102 | 103 | // The params object will be passed directly to the protractor instance, 104 | // and can be accessed from your test. It is an arbitrary object and can 105 | // contain anything you may need in your test. 106 | // This can be changed via the command line as: 107 | // --params.login.user 'Joe' 108 | params: { 109 | login: { 110 | user: 'Jane', 111 | password: '1234' 112 | } 113 | }, 114 | 115 | // ----- The test framework ----- 116 | // 117 | // Jasmine is fully supported as a test and assertion framework. 118 | // Mocha has limited beta support. You will need to include your own 119 | // assertion framework if working with mocha. 120 | framework: 'jasmine', 121 | 122 | // ----- Options to be passed to minijasminenode ----- 123 | // 124 | // See the full list at https://github.com/juliemr/minijasminenode 125 | jasmineNodeOpts: { 126 | // onComplete will be called just before the driver quits. 127 | onComplete: null, 128 | // If true, display spec names. 129 | isVerbose: true, 130 | // If true, print colors to the terminal. 131 | showColors: true, 132 | // If true, include stack traces in failures. 133 | includeStackTrace: true, 134 | // Default time to wait in ms before a test fails. 135 | defaultTimeoutInterval: 30000 136 | }, 137 | 138 | // ----- Options to be passed to mocha ----- 139 | // 140 | // See the full list at http://visionmedia.github.io/mocha/ 141 | mochaOpts: { 142 | ui: 'bdd', 143 | reporter: 'list' 144 | }, 145 | 146 | // ----- The cleanup step ----- 147 | // 148 | // A callback function called once the tests have finished running and 149 | // the webdriver instance has been shut down. It is passed the exit code 150 | // (0 if the tests passed or 1 if not). 151 | onCleanUp: function() {} 152 | }; 153 | -------------------------------------------------------------------------------- /tests/e2e/protractor.saucelabs.conf.js: -------------------------------------------------------------------------------- 1 | // A reference configuration file. 2 | exports.config = { 3 | 4 | // If sauceUser and sauceKey are specified, seleniumServerJar will be ignored. 5 | // The tests will be run remotely using SauceLabs. 6 | sauceUser: process.env.SAUCE_USERNAME, 7 | sauceKey: process.env.SAUCE_ACCESS_KEY, 8 | 9 | // The timeout for each script run on the browser. This should be longer 10 | // than the maximum time your application needs to stabilize between tasks. 11 | allScriptsTimeout: 11000, 12 | 13 | // ----- What tests to run ----- 14 | // 15 | // Spec patterns are relative to the location of this config. 16 | specs: [ 17 | 'specs/*.js', 18 | ], 19 | 20 | multiCapabilities: [{ 21 | 'browserName': 'chrome', 22 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 23 | 'build': process.env.TRAVIS_BUILD_NUMBER, 24 | 'name': 'AngularJS Template (Chrome: Linux) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 25 | 'version': '39', 26 | 'selenium-version': '2.43.1', 27 | 'platform': 'Linux' 28 | }, { 29 | 'browserName': 'firefox', 30 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 31 | 'build': process.env.TRAVIS_BUILD_NUMBER, 32 | 'name': 'AngularJS Template (FF: Linux) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 33 | 'version': '34', 34 | 'selenium-version': '2.43.1', 35 | 'platform': 'Linux' 36 | }, { 37 | 'browserName': 'safari', 38 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 39 | 'build': process.env.TRAVIS_BUILD_NUMBER, 40 | 'name': 'AngularJS Template (Safari: OS X 10.10) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 41 | 'version': '8', 42 | 'selenium-version': '2.43.1', 43 | 'platform': 'OS X 10.10' 44 | }, { 45 | 'browserName': 'internet explorer', 46 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 47 | 'build': process.env.TRAVIS_BUILD_NUMBER, 48 | 'name': 'AngularJS Template (IE11: Win 8.1) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 49 | 'version': '11', 50 | 'selenium-version': '2.43.1', 51 | 'platform': 'Windows 8.1' 52 | }, { 53 | 'browserName': 'internet explorer', 54 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 55 | 'build': process.env.TRAVIS_BUILD_NUMBER, 56 | 'name': 'AngularJS Template (IE10: Win 8) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 57 | 'version': '10', 58 | 'selenium-version': '2.43.1', 59 | 'platform': 'Windows 8' 60 | }, { 61 | 'browserName': 'chrome', 62 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 63 | 'build': process.env.TRAVIS_BUILD_NUMBER, 64 | 'name': 'AngularJS Template (Chrome: Android 5.0) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 65 | 'version': '5.0', 66 | 'platformVersion': '5.0', 67 | 'platformName': 'Android', 68 | 'appiumVersion': '1.3.4', 69 | 'deviceName': 'Android Emulator', 70 | 'device-orientation': 'portrait' 71 | }, { 72 | 'browserName': 'safari', 73 | 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, 74 | 'build': process.env.TRAVIS_BUILD_NUMBER, 75 | 'name': 'AngularJS Template (Safari: iOS 8.1) Build: ' + process.env.TRAVIS_BUILD_NUMBER, 76 | 'version': '8.1', 77 | 'platformVersion': '8.1', 78 | 'platformName': 'iOS', 79 | 'appiumVersion': '1.3.4', 80 | 'platform': 'iOS', 81 | 'deviceName': 'iPhone Simulator', 82 | 'device-orientation': 'portrait' 83 | }], 84 | 85 | // ----- More information for your tests ---- 86 | // 87 | // A base URL for your application under test. Calls to protractor.get() 88 | // with relative paths will be prepended with this. 89 | baseUrl: 'http://127.0.0.1:8000', 90 | 91 | // Selector for the element housing the angular app - this defaults to 92 | // body, but is necessary if ng-app is on a descendant of 93 | rootElement: 'body', 94 | 95 | onPrepare: function() { 96 | browser.getCapabilities().then(function (cap) { 97 | if ((cap.caps_.platform !== "iOS") && (cap.caps_.platform !== "ANDROID")){ 98 | browser.driver.manage().window().setSize(1366, 768) 99 | } 100 | }); 101 | }, 102 | 103 | // ----- The test framework ----- 104 | // 105 | // Jasmine is fully supported as a test and assertion framework. 106 | // Mocha has limited beta support. You will need to include your own 107 | // assertion framework if working with mocha. 108 | framework: 'jasmine', 109 | 110 | // ----- Options to be passed to minijasminenode ----- 111 | // 112 | // See the full list at https://github.com/juliemr/minijasminenode 113 | jasmineNodeOpts: { 114 | // onComplete will be called just before the driver quits. 115 | onComplete: null, 116 | // If true, display spec names. 117 | isVerbose: true, 118 | // If true, print colors to the terminal. 119 | showColors: true, 120 | // If true, include stack traces in failures. 121 | includeStackTrace: true, 122 | // Default time to wait in ms before a test fails. 123 | defaultTimeoutInterval: 30000 124 | } 125 | 126 | }; 127 | -------------------------------------------------------------------------------- /tests/e2e/scripts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /tests/e2e/specs/scenarios.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* https://github.com/angular/protractor/blob/master/docs/getting-started.md */ 4 | 5 | describe("sn.skrollr", function() { 6 | 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /tests/unit/skrollr.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | describe("snSkrollrProvider", function () { 4 | 5 | var serviceProvider, $window, $document, spy, destroy, scope, rootScope, snSkrollr, refresh, skrollrInstance; 6 | 7 | beforeEach(function () { 8 | 9 | // Initialize the service provider by injecting it to a fake module"s config block 10 | angular.module("testApp", []) 11 | .config(function (snSkrollrProvider) { 12 | snSkrollrProvider.config = { smoothScrolling: true }; 13 | snSkrollrProvider.disableMobile = true; 14 | serviceProvider = snSkrollrProvider 15 | }); 16 | 17 | module("sn.skrollr", "testApp"); 18 | 19 | inject(function ($rootScope, $injector) { 20 | scope = $rootScope.$new(); 21 | rootScope = $rootScope; 22 | 23 | snSkrollr = $injector.get("snSkrollr"); 24 | 25 | $window = $injector.get("$window"); 26 | $window.skrollr = { 27 | init: function() { 28 | return { 29 | refresh: function(){}, 30 | destroy: function(){} 31 | }; 32 | } 33 | } 34 | spy = spyOn($window.skrollr, "init"); 35 | spy.and.callThrough(); 36 | 37 | $document = $injector.get("$document"); 38 | $document.ready = function(fn){ fn.call(this); } 39 | 40 | }); 41 | 42 | }); 43 | 44 | it("should configure snSkrollr with options", function () { 45 | expect(serviceProvider.config).toEqual({ smoothScrolling: true }); 46 | }); 47 | 48 | it("should call skrollr init", function () { 49 | rootScope.$$phase = true; 50 | snSkrollr.init({ smoothScrolling: false }); 51 | expect(spy).toHaveBeenCalledWith({ smoothScrolling: false }); 52 | expect(serviceProvider.hasBeenInitialised).toEqual(true); 53 | 54 | snSkrollr.init(); 55 | expect(spy.calls.argsFor(1)).toEqual([{ smoothScrolling: true }]); 56 | expect(serviceProvider.hasBeenInitialised).toEqual(true); 57 | }); 58 | 59 | it("should call destroy on skrollr instance", function () { 60 | 61 | snSkrollr.init(); 62 | destroy = spyOn(serviceProvider.skrollrInstance, "destroy"); 63 | snSkrollr.destroy(); 64 | expect(destroy).toHaveBeenCalled(); 65 | expect(serviceProvider.hasBeenInitialised).toEqual(false); 66 | 67 | }); 68 | 69 | it("should not call destroy if skrollr hasn't been initialised", function () { 70 | 71 | serviceProvider.hasBeenInitialised = false; 72 | snSkrollr.destroy(); 73 | expect(serviceProvider.skrollrInstance.destroy).toBe(undefined); 74 | expect(serviceProvider.hasBeenInitialised).toEqual(false); 75 | 76 | }); 77 | 78 | it("should call refresh on skrollr instance", function () { 79 | 80 | snSkrollr.init(); 81 | refresh = spyOn(serviceProvider.skrollrInstance, "refresh"); 82 | snSkrollr.refresh(); 83 | expect(refresh).toHaveBeenCalled(); 84 | 85 | }); 86 | 87 | it("should NOT call refresh on skrollr instance", function () { 88 | 89 | snSkrollr.init(); 90 | snSkrollr.destroy(); 91 | refresh = spyOn(serviceProvider.skrollrInstance, "refresh"); 92 | snSkrollr.refresh(); 93 | expect(serviceProvider.hasBeenInitialised).toBe(false); 94 | expect(refresh).not.toHaveBeenCalled(); 95 | 96 | }); 97 | 98 | it("should NOT call skrollr init on mobile", function () { 99 | 100 | spy.calls.reset(); 101 | $window.navigator = { 102 | userAgent: "Android" 103 | }; 104 | snSkrollr.init(); 105 | expect(spy).not.toHaveBeenCalled(); 106 | 107 | spy.calls.reset(); 108 | $window.navigator = { 109 | userAgent: "iPad" 110 | }; 111 | snSkrollr.init(); 112 | expect(spy).not.toHaveBeenCalled(); 113 | 114 | spy.calls.reset(); 115 | $window.navigator = { 116 | userAgent: "BlackBerry" 117 | }; 118 | snSkrollr.init(); 119 | expect(spy).not.toHaveBeenCalled(); 120 | 121 | spy.calls.reset(); 122 | $window.navigator = { 123 | userAgent: "Opera Mini" 124 | }; 125 | snSkrollr.init(); 126 | expect(spy).not.toHaveBeenCalled(); 127 | 128 | spy.calls.reset(); 129 | $window.navigator = { 130 | userAgent: "IEMobile" 131 | }; 132 | snSkrollr.init(); 133 | expect(spy).not.toHaveBeenCalled(); 134 | 135 | }); 136 | 137 | }); 138 | 139 | describe("directive: snSkrollr", function() { 140 | var element, scope, isolatedScope, snSkrollr, spy, $window, timeout; 141 | 142 | beforeEach(module("sn.skrollr")); 143 | 144 | beforeEach(inject(function ($rootScope, $compile, $injector) { 145 | scope = $rootScope.$new(); 146 | 147 | snSkrollr = $injector.get("snSkrollr"); 148 | spy = spyOn(snSkrollr, "refresh"); 149 | 150 | $window = $injector.get("$window"); 151 | 152 | timeout = $injector.get("$timeout"); 153 | 154 | element = 155 | "" + 156 | "
" + 157 | "
"; 158 | 159 | element = $compile(element)(scope); 160 | scope.$digest(); 161 | 162 | timeout.flush(); 163 | 164 | })); 165 | 166 | afterEach(function(){ 167 | timeout.verifyNoPendingTasks(); 168 | }) 169 | 170 | it("should refresh skrollr", function (){ 171 | expect(spy).toHaveBeenCalled(); 172 | }); 173 | 174 | it("should refresh skrollr on resize", function (){ 175 | expect(spy.calls.count()).toBe(1); 176 | 177 | angular.element($window).triggerHandler("resize"); 178 | timeout.flush(200); 179 | expect(spy.calls.count()).toBe(2); 180 | 181 | angular.element($window).triggerHandler("resize"); 182 | timeout.flush(200); 183 | expect(spy.calls.count()).toBe(3); 184 | }); 185 | 186 | it("should refresh skrollr on resize", function (){ 187 | expect(spy.calls.count()).toBe(1); 188 | scope.timer = null; 189 | 190 | angular.element($window).triggerHandler("scroll"); 191 | timeout.flush(200); 192 | expect(spy.calls.count()).toBe(2); 193 | 194 | angular.element($window).triggerHandler("scroll"); 195 | timeout.flush(200); 196 | expect(spy.calls.count()).toBe(3); 197 | }); 198 | 199 | }); 200 | 201 | --------------------------------------------------------------------------------