├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .yo-rc.json ├── Gruntfile.js ├── README.md ├── app ├── .buildignore ├── .htaccess ├── 404.html ├── favicon.ico ├── images │ └── yeoman.png ├── index.html ├── robots.txt ├── scripts │ ├── app.js │ ├── controllers │ │ ├── cart.js │ │ ├── category.js │ │ ├── checkout.js │ │ ├── complete.js │ │ ├── main.js │ │ ├── payment.js │ │ ├── product.js │ │ └── store.js │ ├── services │ │ └── moltin.js │ └── vendor │ │ └── moltin.min.js ├── styles │ ├── main.scss │ └── storefront.scss └── views │ ├── cart.html │ ├── category.html │ ├── checkout.html │ ├── complete.html │ ├── main.html │ ├── payment.html │ ├── product.html │ └── store.html ├── bower.json ├── package.json └── test ├── .jshintrc └── spec └── controllers ├── cart.js ├── category.js ├── checkout.js ├── complete.js ├── main.js ├── payment.js ├── product.js └── store.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | .sass-cache 5 | bower_components 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "globals": { 21 | "angular": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2015-03-10 using generator-angular 0.11.1 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Load grunt tasks automatically 13 | require('load-grunt-tasks')(grunt); 14 | 15 | // Time how long tasks take. Can help when optimizing build times 16 | require('time-grunt')(grunt); 17 | 18 | // Configurable paths for the application 19 | var appConfig = { 20 | app: require('./bower.json').appPath || 'app', 21 | dist: 'dist' 22 | }; 23 | 24 | // Define the configuration for all the tasks 25 | grunt.initConfig({ 26 | 27 | // Project settings 28 | yeoman: appConfig, 29 | 30 | // Watches files for changes and runs tasks based on the changed files 31 | watch: { 32 | bower: { 33 | files: ['bower.json'], 34 | tasks: ['wiredep'] 35 | }, 36 | js: { 37 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'], 38 | tasks: ['newer:jshint:all'], 39 | options: { 40 | livereload: '<%= connect.options.livereload %>' 41 | } 42 | }, 43 | jsTest: { 44 | files: ['test/spec/{,*/}*.js'], 45 | tasks: ['newer:jshint:test', 'karma'] 46 | }, 47 | compass: { 48 | files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], 49 | tasks: ['compass:server', 'autoprefixer'] 50 | }, 51 | gruntfile: { 52 | files: ['Gruntfile.js'] 53 | }, 54 | livereload: { 55 | options: { 56 | livereload: '<%= connect.options.livereload %>' 57 | }, 58 | files: [ 59 | '<%= yeoman.app %>/{,*/}*.html', 60 | '.tmp/styles/{,*/}*.css', 61 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 62 | ] 63 | } 64 | }, 65 | 66 | // The actual grunt server settings 67 | connect: { 68 | options: { 69 | port: 9000, 70 | // Change this to '0.0.0.0' to access the server from outside. 71 | hostname: 'localhost', 72 | livereload: 35729 73 | }, 74 | livereload: { 75 | options: { 76 | open: true, 77 | middleware: function (connect) { 78 | return [ 79 | connect.static('.tmp'), 80 | connect().use( 81 | '/bower_components', 82 | connect.static('./bower_components') 83 | ), 84 | connect().use( 85 | '/app/styles', 86 | connect.static('./app/styles') 87 | ), 88 | connect.static(appConfig.app) 89 | ]; 90 | } 91 | } 92 | }, 93 | test: { 94 | options: { 95 | port: 9001, 96 | middleware: function (connect) { 97 | return [ 98 | connect.static('.tmp'), 99 | connect.static('test'), 100 | connect().use( 101 | '/bower_components', 102 | connect.static('./bower_components') 103 | ), 104 | connect.static(appConfig.app) 105 | ]; 106 | } 107 | } 108 | }, 109 | dist: { 110 | options: { 111 | open: true, 112 | base: '<%= yeoman.dist %>' 113 | } 114 | } 115 | }, 116 | 117 | // Make sure code styles are up to par and there are no obvious mistakes 118 | jshint: { 119 | options: { 120 | jshintrc: '.jshintrc', 121 | reporter: require('jshint-stylish') 122 | }, 123 | all: { 124 | src: [ 125 | 'Gruntfile.js', 126 | '<%= yeoman.app %>/scripts/{,*/}*.js' 127 | ] 128 | }, 129 | test: { 130 | options: { 131 | jshintrc: 'test/.jshintrc' 132 | }, 133 | src: ['test/spec/{,*/}*.js'] 134 | } 135 | }, 136 | 137 | // Empties folders to start fresh 138 | clean: { 139 | dist: { 140 | files: [{ 141 | dot: true, 142 | src: [ 143 | '.tmp', 144 | '<%= yeoman.dist %>/{,*/}*', 145 | '!<%= yeoman.dist %>/.git{,*/}*' 146 | ] 147 | }] 148 | }, 149 | server: '.tmp' 150 | }, 151 | 152 | // Add vendor prefixed styles 153 | autoprefixer: { 154 | options: { 155 | browsers: ['last 1 version'] 156 | }, 157 | server: { 158 | options: { 159 | map: true, 160 | }, 161 | files: [{ 162 | expand: true, 163 | cwd: '.tmp/styles/', 164 | src: '{,*/}*.css', 165 | dest: '.tmp/styles/' 166 | }] 167 | }, 168 | dist: { 169 | files: [{ 170 | expand: true, 171 | cwd: '.tmp/styles/', 172 | src: '{,*/}*.css', 173 | dest: '.tmp/styles/' 174 | }] 175 | } 176 | }, 177 | 178 | // Automatically inject Bower components into the app 179 | wiredep: { 180 | app: { 181 | src: ['<%= yeoman.app %>/index.html'], 182 | ignorePath: /\.\.\// 183 | }, 184 | test: { 185 | devDependencies: true, 186 | src: '<%= karma.unit.configFile %>', 187 | ignorePath: /\.\.\//, 188 | fileTypes:{ 189 | js: { 190 | block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi, 191 | detect: { 192 | js: /'(.*\.js)'/gi 193 | }, 194 | replace: { 195 | js: '\'{{filePath}}\',' 196 | } 197 | } 198 | } 199 | }, 200 | sass: { 201 | src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], 202 | ignorePath: /(\.\.\/){1,2}bower_components\// 203 | } 204 | }, 205 | 206 | // Compiles Sass to CSS and generates necessary files if requested 207 | compass: { 208 | options: { 209 | sassDir: '<%= yeoman.app %>/styles', 210 | cssDir: '.tmp/styles', 211 | generatedImagesDir: '.tmp/images/generated', 212 | imagesDir: '<%= yeoman.app %>/images', 213 | javascriptsDir: '<%= yeoman.app %>/scripts', 214 | fontsDir: '<%= yeoman.app %>/styles/fonts', 215 | importPath: './bower_components', 216 | httpImagesPath: '/images', 217 | httpGeneratedImagesPath: '/images/generated', 218 | httpFontsPath: '/styles/fonts', 219 | relativeAssets: false, 220 | assetCacheBuster: false, 221 | raw: 'Sass::Script::Number.precision = 10\n' 222 | }, 223 | dist: { 224 | options: { 225 | generatedImagesDir: '<%= yeoman.dist %>/images/generated' 226 | } 227 | }, 228 | server: { 229 | options: { 230 | sourcemap: true 231 | } 232 | } 233 | }, 234 | 235 | // Renames files for browser caching purposes 236 | filerev: { 237 | dist: { 238 | src: [ 239 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 240 | '<%= yeoman.dist %>/styles/{,*/}*.css', 241 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 242 | '<%= yeoman.dist %>/styles/fonts/*' 243 | ] 244 | } 245 | }, 246 | 247 | // Reads HTML for usemin blocks to enable smart builds that automatically 248 | // concat, minify and revision files. Creates configurations in memory so 249 | // additional tasks can operate on them 250 | useminPrepare: { 251 | html: '<%= yeoman.app %>/index.html', 252 | options: { 253 | dest: '<%= yeoman.dist %>', 254 | flow: { 255 | html: { 256 | steps: { 257 | js: ['concat', 'uglifyjs'], 258 | css: ['cssmin'] 259 | }, 260 | post: {} 261 | } 262 | } 263 | } 264 | }, 265 | 266 | // Performs rewrites based on filerev and the useminPrepare configuration 267 | usemin: { 268 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 269 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 270 | options: { 271 | assetsDirs: [ 272 | '<%= yeoman.dist %>', 273 | '<%= yeoman.dist %>/images', 274 | '<%= yeoman.dist %>/styles' 275 | ] 276 | } 277 | }, 278 | 279 | // The following *-min tasks will produce minified files in the dist folder 280 | // By default, your `index.html`'s will take care of 281 | // minification. These next options are pre-configured if you do not wish 282 | // to use the Usemin blocks. 283 | // cssmin: { 284 | // dist: { 285 | // files: { 286 | // '<%= yeoman.dist %>/styles/main.css': [ 287 | // '.tmp/styles/{,*/}*.css' 288 | // ] 289 | // } 290 | // } 291 | // }, 292 | // uglify: { 293 | // dist: { 294 | // files: { 295 | // '<%= yeoman.dist %>/scripts/scripts.js': [ 296 | // '<%= yeoman.dist %>/scripts/scripts.js' 297 | // ] 298 | // } 299 | // } 300 | // }, 301 | // concat: { 302 | // dist: {} 303 | // }, 304 | 305 | imagemin: { 306 | dist: { 307 | files: [{ 308 | expand: true, 309 | cwd: '<%= yeoman.app %>/images', 310 | src: '{,*/}*.{png,jpg,jpeg,gif}', 311 | dest: '<%= yeoman.dist %>/images' 312 | }] 313 | } 314 | }, 315 | 316 | svgmin: { 317 | dist: { 318 | files: [{ 319 | expand: true, 320 | cwd: '<%= yeoman.app %>/images', 321 | src: '{,*/}*.svg', 322 | dest: '<%= yeoman.dist %>/images' 323 | }] 324 | } 325 | }, 326 | 327 | htmlmin: { 328 | dist: { 329 | options: { 330 | collapseWhitespace: true, 331 | conservativeCollapse: true, 332 | collapseBooleanAttributes: true, 333 | removeCommentsFromCDATA: true, 334 | removeOptionalTags: true 335 | }, 336 | files: [{ 337 | expand: true, 338 | cwd: '<%= yeoman.dist %>', 339 | src: ['*.html', 'views/{,*/}*.html'], 340 | dest: '<%= yeoman.dist %>' 341 | }] 342 | } 343 | }, 344 | 345 | // ng-annotate tries to make the code safe for minification automatically 346 | // by using the Angular long form for dependency injection. 347 | ngAnnotate: { 348 | dist: { 349 | files: [{ 350 | expand: true, 351 | cwd: '.tmp/concat/scripts', 352 | src: '*.js', 353 | dest: '.tmp/concat/scripts' 354 | }] 355 | } 356 | }, 357 | 358 | // Replace Google CDN references 359 | cdnify: { 360 | dist: { 361 | html: ['<%= yeoman.dist %>/*.html'] 362 | } 363 | }, 364 | 365 | // Copies remaining files to places other tasks can use 366 | copy: { 367 | dist: { 368 | files: [{ 369 | expand: true, 370 | dot: true, 371 | cwd: '<%= yeoman.app %>', 372 | dest: '<%= yeoman.dist %>', 373 | src: [ 374 | '*.{ico,png,txt}', 375 | '.htaccess', 376 | '*.html', 377 | 'views/{,*/}*.html', 378 | 'images/{,*/}*.{webp}', 379 | 'styles/fonts/{,*/}*.*' 380 | ] 381 | }, { 382 | expand: true, 383 | cwd: '.tmp/images', 384 | dest: '<%= yeoman.dist %>/images', 385 | src: ['generated/*'] 386 | }, { 387 | expand: true, 388 | cwd: '.', 389 | src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*', 390 | dest: '<%= yeoman.dist %>' 391 | }] 392 | }, 393 | styles: { 394 | expand: true, 395 | cwd: '<%= yeoman.app %>/styles', 396 | dest: '.tmp/styles/', 397 | src: '{,*/}*.css' 398 | } 399 | }, 400 | 401 | // Run some tasks in parallel to speed up the build process 402 | concurrent: { 403 | server: [ 404 | 'compass:server' 405 | ], 406 | test: [ 407 | 'compass' 408 | ], 409 | dist: [ 410 | 'compass:dist', 411 | 'imagemin', 412 | 'svgmin' 413 | ] 414 | }, 415 | 416 | // Test settings 417 | karma: { 418 | unit: { 419 | configFile: 'test/karma.conf.js', 420 | singleRun: true 421 | } 422 | } 423 | }); 424 | 425 | 426 | grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { 427 | if (target === 'dist') { 428 | return grunt.task.run(['build', 'connect:dist:keepalive']); 429 | } 430 | 431 | grunt.task.run([ 432 | 'clean:server', 433 | 'wiredep', 434 | 'concurrent:server', 435 | 'autoprefixer:server', 436 | 'connect:livereload', 437 | 'watch' 438 | ]); 439 | }); 440 | 441 | grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) { 442 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 443 | grunt.task.run(['serve:' + target]); 444 | }); 445 | 446 | grunt.registerTask('test', [ 447 | 'clean:server', 448 | 'wiredep', 449 | 'concurrent:test', 450 | 'autoprefixer', 451 | 'connect:test', 452 | 'karma' 453 | ]); 454 | 455 | grunt.registerTask('build', [ 456 | 'clean:dist', 457 | 'wiredep', 458 | 'useminPrepare', 459 | 'concurrent:dist', 460 | 'autoprefixer', 461 | 'concat', 462 | 'ngAnnotate', 463 | 'copy:dist', 464 | 'cdnify', 465 | 'cssmin', 466 | 'uglify', 467 | 'filerev', 468 | 'usemin', 469 | 'htmlmin' 470 | ]); 471 | 472 | grunt.registerTask('default', [ 473 | 'newer:jshint', 474 | 'test', 475 | 'build' 476 | ]); 477 | }; 478 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # storefront 2 | 3 | This project is generated with [yo angular generator](https://github.com/yeoman/generator-angular) 4 | version 0.11.1. 5 | 6 | ## Build & development 7 | 8 | Run `grunt` for building and `grunt serve` for preview. 9 | 10 | ## Testing 11 | 12 | Running `grunt test` will run the unit tests with karma. 13 | -------------------------------------------------------------------------------- /app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learncodeacademy/moltin-angular/541cac65e35e09f4874811cd52fe4c6bb516baf7/app/favicon.ico -------------------------------------------------------------------------------- /app/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learncodeacademy/moltin-angular/541cac65e35e09f4874811cd52fe4c6bb516baf7/app/images/yeoman.png -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 |
25 | 55 |
56 | 57 |
58 |
59 |
60 | 61 | 66 | 67 | 68 | 69 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc overview 5 | * @name storefrontApp 6 | * @description 7 | * # storefrontApp 8 | * 9 | * Main module of the application. 10 | */ 11 | angular 12 | .module('storefrontApp', [ 13 | 'storefrontApp.moltin', 14 | 'ngAnimate', 15 | 'ngCookies', 16 | 'ngResource', 17 | 'ngRoute', 18 | 'ngSanitize', 19 | 'ngTouch' 20 | ]) 21 | .config(function ($routeProvider) { 22 | $routeProvider 23 | .when('/', { 24 | templateUrl: 'views/main.html', 25 | controller: 'MainCtrl' 26 | }) 27 | .when('/about', { 28 | templateUrl: 'views/about.html', 29 | controller: 'AboutCtrl' 30 | }) 31 | .when('/store', { 32 | templateUrl: 'views/store.html', 33 | controller: 'StoreCtrl', 34 | resolve: { 35 | categories: function($q, MoltinAuth) { 36 | var deferred = $q.defer(); 37 | MoltinAuth.then(function(moltin) { 38 | moltin.Category.List(null, function(categories) { 39 | deferred.resolve(categories); 40 | }); 41 | }) 42 | return deferred.promise; 43 | }, 44 | features: function($q, MoltinAuth) { 45 | var deferred = $q.defer(); 46 | MoltinAuth.then(function(moltin) { 47 | //"Homepage Features" collection 48 | moltin.Product.List({collection:'947420948187841171'}, function(collection) { 49 | deferred.resolve(collection); 50 | }); 51 | }); 52 | return deferred.promise; 53 | } 54 | } 55 | }) 56 | .when('/category/:id', { 57 | templateUrl: 'views/category.html', 58 | controller: 'CategoryCtrl', 59 | resolve: { 60 | category: function($q, $route, MoltinAuth) { 61 | var deferred = $q.defer(); 62 | MoltinAuth.then(function(moltin) { 63 | moltin.Category.Get($route.current.params.id, function(category) { 64 | deferred.resolve(category); 65 | }); 66 | }) 67 | return deferred.promise; 68 | }, 69 | products: function($q, $route, MoltinAuth) { 70 | var deferred = $q.defer(); 71 | MoltinAuth.then(function(moltin) { 72 | moltin.Product.List({category: $route.current.params.id}, function(products) { 73 | deferred.resolve(products); 74 | }); 75 | }) 76 | return deferred.promise; 77 | } 78 | } 79 | }) 80 | .when('/product/:id', { 81 | templateUrl: 'views/product.html', 82 | controller: 'ProductCtrl', 83 | resolve: { 84 | product: function($q, $route, MoltinAuth) { 85 | var deferred = $q.defer(); 86 | MoltinAuth.then(function(moltin) { 87 | moltin.Product.Get($route.current.params.id, function(product) { 88 | deferred.resolve(product); 89 | }); 90 | }) 91 | return deferred.promise; 92 | }, 93 | moltin: function(MoltinAuth) { 94 | return MoltinAuth; 95 | } 96 | } 97 | }) 98 | .when('/cart', { 99 | templateUrl: 'views/cart.html', 100 | controller: 'CartCtrl', 101 | resolve: { 102 | cart: function($q, MoltinAuth) { 103 | var deferred = $q.defer(); 104 | MoltinAuth.then(function(moltin) { 105 | moltin.Cart.Contents(function(cart) { 106 | deferred.resolve(cart); 107 | }); 108 | }) 109 | return deferred.promise; 110 | }, 111 | } 112 | }) 113 | .when('/checkout', { 114 | templateUrl: 'views/checkout.html', 115 | controller: 'CheckoutCtrl', 116 | resolve: { 117 | cart: function($q, MoltinAuth) { 118 | var deferred = $q.defer(); 119 | MoltinAuth.then(function(moltin) { 120 | moltin.Cart.Contents(function(cart) { 121 | deferred.resolve(cart); 122 | }); 123 | }) 124 | return deferred.promise; 125 | }, 126 | options: function($q, MoltinAuth) { 127 | var deferred = $q.defer(); 128 | MoltinAuth.then(function(moltin) { 129 | moltin.Cart.Checkout(function(options) { 130 | deferred.resolve(options); 131 | }); 132 | }) 133 | return deferred.promise; 134 | }, 135 | fields: function($q, MoltinAuth) { 136 | var deferred = $q.defer(); 137 | MoltinAuth.then(function(moltin) { 138 | moltin.Address.Fields(null, null, function(fields) { 139 | deferred.resolve(fields); 140 | }); 141 | }) 142 | return deferred.promise; 143 | }, 144 | moltin: function(MoltinAuth) { 145 | return MoltinAuth; 146 | } 147 | } 148 | }) 149 | .when('/payment', { 150 | templateUrl: 'views/payment.html', 151 | controller: 'PaymentCtrl', 152 | resolve: { 153 | moltin: function($q, MoltinAuth) { 154 | return MoltinAuth; 155 | } 156 | } 157 | }) 158 | .when('/complete', { 159 | templateUrl: 'views/complete.html', 160 | controller: 'CompleteCtrl' 161 | }) 162 | .otherwise({ 163 | redirectTo: '/' 164 | }); 165 | }).run(function(MoltinAuth, $rootScope) { 166 | MoltinAuth.then(function(moltin) { 167 | moltin.Cart.Contents(function(items) { 168 | $rootScope.cart = items; 169 | $rootScope.$apply(); 170 | }); 171 | }); 172 | }); 173 | -------------------------------------------------------------------------------- /app/scripts/controllers/cart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:CartCtrl 6 | * @description 7 | * # CartCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('CartCtrl', function ($scope, cart) { 12 | $scope.cart = cart; 13 | console.log(cart); 14 | }); 15 | -------------------------------------------------------------------------------- /app/scripts/controllers/category.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:CategoryCtrl 6 | * @description 7 | * # CategoryCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('CategoryCtrl', function ($scope, category, products) { 12 | $scope.category = category; 13 | $scope.products = products; 14 | console.log(category, products); 15 | }); 16 | -------------------------------------------------------------------------------- /app/scripts/controllers/checkout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:CheckoutCtrl 6 | * @description 7 | * # CheckoutCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('CheckoutCtrl', function ($scope, $rootScope, $location, moltin, cart, options, fields) { 12 | $scope.data = {bill: {}, ship: {}, ship_bill: 0, notes: '', shipping: '', gateway: ''} 13 | $scope.cart = cart; 14 | $scope.options = options; 15 | $scope.fields = fields; 16 | 17 | $scope.createOrder = function() { 18 | moltin.Cart.Order({ 19 | customer: '947445503480562327', //guest customer for now 20 | shipping: $scope.data.shipping, 21 | gateway: $scope.data.gateway, 22 | bill_to: $scope.data.bill, 23 | ship_to: $scope.data.ship_bill ? 'bill_to' : $scope.data.ship 24 | }, function(response) { 25 | $rootScope.order = response; 26 | $rootScope.$apply(function() { 27 | $location.path('/payment'); 28 | }); 29 | }) 30 | } 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /app/scripts/controllers/complete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:CompleteCtrl 6 | * @description 7 | * # CompleteCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('CompleteCtrl', function ($scope) { 12 | $scope.awesomeThings = [ 13 | 'HTML5 Boilerplate', 14 | 'AngularJS', 15 | 'Karma' 16 | ]; 17 | }); 18 | -------------------------------------------------------------------------------- /app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:MainCtrl 6 | * @description 7 | * # MainCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('MainCtrl', function ($scope) { 12 | $scope.awesomeThings = [ 13 | 'HTML5 Boilerplate', 14 | 'AngularJS', 15 | 'Karma' 16 | ]; 17 | }); 18 | -------------------------------------------------------------------------------- /app/scripts/controllers/payment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:PaymentCtrl 6 | * @description 7 | * # PaymentCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('PaymentCtrl', function ($scope, $location, $rootScope, moltin) { 12 | $scope.payment = function(data) { 13 | moltin.Checkout.Payment('purchase', $scope.order.id, {data: $scope.data}, function(response) { 14 | $rootScope.order = $rootScope.cart = null; 15 | $rootScope.payment = response; 16 | $rootScope.$apply(function() { 17 | $location.path('/complete'); 18 | }); 19 | }); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /app/scripts/controllers/product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:ProductCtrl 6 | * @description 7 | * # ProductCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('ProductCtrl', function ($scope, $rootScope, product, moltin, $timeout) { 12 | $scope.product = product; 13 | $scope.addStatus = null; 14 | 15 | $scope.addCart = function() { 16 | $scope.addStatus = 'Adding to cart...'; 17 | // Insert(id, qty, modifiers/size, callback) 18 | moltin.Cart.Insert(product.id, 1, null, function(cart) { 19 | $scope.addStatus = 'Success!'; 20 | moltin.Cart.Contents(function(items) { 21 | $rootScope.cart = items; 22 | $rootScope.$apply(); 23 | }); 24 | $scope.$apply(); 25 | $timeout(function() { 26 | $scope.addStatus = null; 27 | }, 1000); 28 | }); 29 | } 30 | console.log(product); 31 | }); 32 | -------------------------------------------------------------------------------- /app/scripts/controllers/store.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name storefrontApp.controller:StoreCtrl 6 | * @description 7 | * # StoreCtrl 8 | * Controller of the storefrontApp 9 | */ 10 | angular.module('storefrontApp') 11 | .controller('StoreCtrl', function ($scope, categories, features) { 12 | $scope.categories = categories; 13 | $scope.features = features; 14 | console.log(features); 15 | }); 16 | -------------------------------------------------------------------------------- /app/scripts/services/moltin.js: -------------------------------------------------------------------------------- 1 | angular.module('storefrontApp.moltin', []) 2 | .factory('MoltinAuth', function($q) { 3 | var deferred = $q.defer(); 4 | var moltin = new Moltin({publicId: 'hHr6QhXDTz9XigXiyPzKaaeLEHLHJJXhychmd186'}); 5 | moltin.Authenticate(function() { 6 | deferred.resolve(moltin); 7 | }); 8 | 9 | return deferred.promise; 10 | }); 11 | -------------------------------------------------------------------------------- /app/scripts/vendor/moltin.min.js: -------------------------------------------------------------------------------- 1 | /*! moltin-sdk minified - v0.6.0 - 2015-02-15 */ 2 | 3 | var Moltin,__indexOf=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};Moltin=function(){"use strict";function a(a){this.options=this.Merge(this.options,a),this.Storage=new o,this.Address=new b(this),this.Brand=new c(this),this.Cart=new d(this),this.Category=new e(this),this.Checkout=new f(this),this.Collection=new g(this),this.Currency=new h(this),this.Entry=new j(this),this.Gateway=new k(this),this.Order=new l(this),this.Product=new m(this),this.Shipping=new n(this),this.Tax=new p(this),this.Storage.get("_mc")&&(this.options.currency=this.Storage.get("_mc")),this.Storage.get("_ms")||this.Storage.set("_ms",this.Cart.CreateIdentifier())}var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;return a.prototype.options={publicId:"",auth:{},url:"https://api.molt.in/",version:"beta",debug:!1,currency:!1,notice:function(a,b){return alert(a+": "+b)},methods:["GET","POST","PUT","DELETE"]},a.prototype.Merge=function(a,b){var c,d,e;d={};for(c in a)e=a[c],d[c]=e;for(c in b)e=b[c],d[c]=e;return d},a.prototype.InArray=function(a,b){return __indexOf.call(b,a)<0?!1:!0},a.prototype.Serialize=function(a,b){var c,d,e;null==b&&(b=null),d=[];for(c in a)e=a[c],c=null!==b?b+"["+c+"]":c,d.push("object"==typeof e?this.Serialize(e,c):encodeURIComponent(c)+"="+encodeURIComponent(e));return d.join("&")},a.prototype.Error=function(a){var b,c,d,e;if(c="","undefind"!=typeof a.errors){e=a.errors;for(b in e)d=e[b],c+=d+"
"}else c=a.error;return this.options.notice("Error",c)},a.prototype.Ajax=function(a){var b,c,d,e,f,g,h;b={type:"GET",async:!1,data:null,timeout:5e3,headers:{},url:this.options.url+this.options.version,success:function(){},error:function(){}},b=this.Merge(b,a),b.type=b.type.toUpperCase();try{e=new XMLHttpRequest}catch(i){c=i;try{e=new ActiveXObject("Msxml2.XMLHTTP")}catch(i){return c=i,!1}}"GET"===b.type?(b.url+="?"+this.Serialize(b.data),b.data=null):b.data=this.Serialize(b.data),e.open(b.type,b.url,b.async),f=setTimeout(function(a){return function(){return e.abort(),b.error(a.options.notice("error","Your request timed out"))}}(this),b.timeout),h=b.headers;for(d in h)g=h[d],e.setRequestHeader(d,g);return e.onreadystatechange=function(){var a;return 4!==e.readyState?null:(clearTimeout(f),a=JSON.parse(e.responseText),"2"!==e.status.toString().charAt(0)?b.error(e,e.status,a):b.success(a,e.status,e))},e.send(b.data)},a.prototype.Authenticate=function(a){var b;return this.options.publicId.length<=0?this.options.notice("error","Public ID must be set"):null!==this.Storage.get("_mt")&&parseInt(this.Storage.get("_me"))>Date.now()?(this.options.auth={token:this.Storage.get("_mt"),expires:this.Storage.get("_me")},"function"==typeof a&&a(this.options.auth),b=new CustomEvent("MoltinReady",{detail:this}),window.dispatchEvent(b),this):(this.Ajax({type:"POST",url:this.options.url+"oauth/access_token",data:{grant_type:"implicit",client_id:this.options.publicId},async:"function"==typeof a?!0:!1,headers:{"Content-Type":"application/x-www-form-urlencoded"},success:function(c){return function(d){return c.options.auth={token:d.access_token,expires:1e3*parseInt(d.expires)},c.Storage.set("_mt",d.access_token),c.Storage.set("_me",c.options.auth.expires),"function"==typeof a&&a(d),b=new CustomEvent("MoltinReady",{detail:c}),window.dispatchEvent(b)}}(this),error:function(a){return function(){return a.options.notice("error","Authorization failed")}}(this)}),this)},a.prototype.Request=function(a,b,c,d){var e,f;return null==b&&(b="GET"),null==c&&(c=null),e={},f={"Content-Type":"application/x-www-form-urlencoded",Authorization:"Bearer "+this.options.auth.token,"X-Session":this.Storage.get("_ms"),"X-Location":window.location.href},null===this.options.auth.token?this.options.notice("error","You must authenticate first"):(Date.now()>parseInt(this.Storage.get("_me"))&&this.Authenticate(),this.InArray(b,this.options.methods)?(this.options.currency&&(f["X-Currency"]=this.options.currency),this.Ajax({type:b,url:this.options.url+this.options.version+"/"+a,data:c,async:"function"==typeof d?!0:!1,headers:f,success:function(){return function(a){return"function"==typeof d?d(a.result,"undefined"!=typeof a.pagination?a.pagination:null):e=a}}(this),error:function(a){return function(b,c){var d;return d=JSON.parse(b.responseText),d.status===!1&&a.options.notice("error","undefined"!=typeof d.errors?d.errors:d.error,c),e=d}}(this)}),"undefined"==typeof d?e.result:void 0):this.options.notice("error","Invalid request method ("+b+")"))},o=function(){function a(){}return a.prototype.set=function(a,b,c){var d,e;return e="",c&&(d=new Date,d.setTime(d.getTime()+24*c*60*60*1e3),e="; expires="+d.toGMTString()),document.cookie=a+"="+b+e+"; path=/"},a.prototype.get=function(a){var b,c,d,e;for(a+="=",e=document.cookie.split(";"),c=0,d=e.length;d>c;c++){for(b=e[c];" "===b.charAt(0);)b=b.substring(1,b.length);if(0===b.indexOf(a))return b.substring(a.length,b.length)}return null},a.prototype.remove=function(a){return this.set(a,"",-1)},a}(),b=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b,c){return this.m.Request("customer/"+a+"/address/"+b,"GET",null,c)},a.prototype.Find=function(a,b,c){return this.m.Request("customer/"+a+"/address","GET",b,c)},a.prototype.List=function(a,b,c){return this.m.Request("customer/"+a+"/addresses","GET",b,c)},a.prototype.Create=function(a,b,c){return this.m.Request("customer/"+a+"/address","POST",b,c)},a.prototype.Fields=function(a,b,c){var d;return null==a&&(a=0),null==b&&(b=0),d=a>0&&0>=b?"customer/"+a+"/address/fields":a>0&&b>0?"customer/"+a+"/address/"+b+"/fields":"address/fields",this.m.Request(d,"GET",null,c)},a}(),c=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("brand/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("brand","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("brands","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="brand/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a}(),d=function(){function a(a){this.m=a,this.identifier=this.GetIdentifier()}return a.prototype.CreateIdentifier=function(){return"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/[x]/g,function(){return(16*Math.random()|0).toString(16)})},a.prototype.GetIdentifier=function(){var a;return null!==this.m.Storage.get("_mb")?this.m.Storage.get("_mb"):(a=this.CreateIdentifier(),this.m.Storage.set("_mb",a),a)},a.prototype.Contents=function(a){return this.m.Request("cart/"+this.identifier,"GET",null,a)},a.prototype.Insert=function(a,b,c,d){return null==b&&(b=1),null==c&&(c=null),this.m.Request("cart/"+this.identifier,"POST",{id:a,quantity:b,modifier:c},d)},a.prototype.Update=function(a,b,c){return this.m.Request("cart/"+this.identifier+"/item/"+a,"PUT",b,c)},a.prototype.Delete=function(a){return this.m.Request("cart/"+this.identifier,"DELETE",null,a)},a.prototype.Remove=function(a,b){return this.m.Request("cart/"+this.identifier+"/item/"+a,"DELETE",null,b)},a.prototype.Item=function(a,b){return this.m.Request("cart/"+this.identifier+"/item/"+a,"GET",null,b)},a.prototype.InCart=function(a,b){return this.m.Request("cart/"+this.identifier+"/has/"+a,"GET",null,b)},a.prototype.Checkout=function(a){return this.m.Request("cart/"+this.identifier+"/checkout","GET",null,a)},a.prototype.Order=function(a,b){return this.m.Request("cart/"+this.identifier+"/checkout","POST",a,b)},a}(),e=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("category/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("category","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("categories","GET",a,b)},a.prototype.Tree=function(a,b){return this.m.Request("categories/tree","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="category/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a}(),f=function(){function a(a){this.m=a}return a.prototype.Payment=function(a,b,c,d){return this.m.Request("checkout/payment/"+a+"/"+b,"POST",c,d)},a}(),g=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("collection/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("collection","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("collections","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="collection/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a}(),h=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("currency/"+a,"GET",null,b)},a.prototype.Set=function(a,b){return this.m.Storage.set("_mc",a),this.m.options.currency=a,"function"==typeof b?b(a):void 0},a.prototype.Find=function(a,b){return this.m.Request("currency","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("currencies","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="currency/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a}(),i=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("customer/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("customer","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("customers","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="customer/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a}(),j=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b,c){return this.m.Request("flow/"+a+"/entry/"+b,"GET",null,c)},a.prototype.Find=function(a,b,c){return this.m.Request("flow/"+a+"/entry","GET",b,c)},a.prototype.List=function(a,b,c){return this.m.Request("flow/"+a+"/entries","GET",b,c)},a}(),k=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("gateway/"+a,"GET",null,b)},a.prototype.List=function(a,b){return this.m.Request("gateways","GET",a,b)},a}(),l=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("order/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("order","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("orders","GET",a,b)},a.prototype.Create=function(a,b){return this.m.Request("order","POST",a,b)},a}(),m=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("product/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("product","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("products","GET",a,b)},a.prototype.Search=function(a,b){return this.m.Request("products/search","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="product/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a.prototype.Modifiers=function(a,b){return this.m.Request("product/"+a+"/modifiers","GET",null,b)},a.prototype.Variations=function(a){return this.m.Request("product/"+a+"/variations","GET",null,callback)},a}(),n=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("shipping/"+a,"GET",null,b)},a.prototype.List=function(a,b){return this.m.Request("shipping","GET",a,b)},a}(),p=function(){function a(a){this.m=a}return a.prototype.Get=function(a,b){return this.m.Request("tax/"+a,"GET",null,b)},a.prototype.Find=function(a,b){return this.m.Request("tax","GET",a,b)},a.prototype.List=function(a,b){return this.m.Request("taxes","GET",a,b)},a.prototype.Fields=function(a,b){var c;return null==a&&(a=0),c="tax/"+(0!==a?a+"/fields":"fields"),this.m.Request(c,"GET",null,b)},a}(),a}(); 4 | -------------------------------------------------------------------------------- /app/styles/main.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: "../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/"; 2 | // bower:scss 3 | @import "bootstrap-sass-official/assets/stylesheets/_bootstrap.scss"; 4 | // endbower 5 | 6 | .browsehappy { 7 | margin: 0.2em 0; 8 | background: #ccc; 9 | color: #000; 10 | padding: 0.2em 0; 11 | } 12 | 13 | body { 14 | padding: 0; 15 | } 16 | 17 | /* Everything but the jumbotron gets side spacing for mobile first views */ 18 | .header, 19 | .marketing, 20 | .footer { 21 | padding-left: 15px; 22 | padding-right: 15px; 23 | } 24 | 25 | /* Custom page header */ 26 | .header { 27 | border-bottom: 1px solid #e5e5e5; 28 | margin-bottom: 10px; 29 | 30 | /* Make the masthead heading the same height as the navigation */ 31 | h3 { 32 | margin-top: 0; 33 | margin-bottom: 0; 34 | line-height: 40px; 35 | padding-bottom: 19px; 36 | } 37 | } 38 | 39 | /* Custom page footer */ 40 | .footer { 41 | padding-top: 19px; 42 | color: #777; 43 | border-top: 1px solid #e5e5e5; 44 | } 45 | 46 | .container-narrow > hr { 47 | margin: 30px 0; 48 | } 49 | 50 | /* Main marketing message and sign up button */ 51 | .jumbotron { 52 | text-align: center; 53 | border-bottom: 1px solid #e5e5e5; 54 | 55 | .btn { 56 | font-size: 21px; 57 | padding: 14px 24px; 58 | } 59 | } 60 | 61 | /* Supporting marketing content */ 62 | .marketing { 63 | margin: 40px 0; 64 | 65 | p + h4 { 66 | margin-top: 28px; 67 | } 68 | } 69 | 70 | /* Responsive: Portrait tablets and up */ 71 | @media screen and (min-width: 768px) { 72 | .container { 73 | max-width: 730px; 74 | } 75 | 76 | /* Remove the padding we set earlier */ 77 | .header, 78 | .marketing, 79 | .footer { 80 | padding-left: 0; 81 | padding-right: 0; 82 | } 83 | /* Space out the masthead */ 84 | .header { 85 | margin-bottom: 30px; 86 | } 87 | /* Remove the bottom border on the jumbotron for visual effect */ 88 | .jumbotron { 89 | border-bottom: 0; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/styles/storefront.scss: -------------------------------------------------------------------------------- 1 | #cart { 2 | margin-top: 10px; 3 | } 4 | -------------------------------------------------------------------------------- /app/views/cart.html: -------------------------------------------------------------------------------- 1 |

Your Cart:

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
ItemQtyPrice
{{product.name}}{{product.quantity}}{{product.pricing.formatted.without_tax}}
19 | 20 |

Total Price: {{cart.totals.formatted.with_tax}}

21 | 22 | Checkout! 23 | -------------------------------------------------------------------------------- /app/views/category.html: -------------------------------------------------------------------------------- 1 |

{{ category.title }}

2 | 3 |
4 |
5 | 6 | {{prod.title}} 7 |

{{ prod.title }}

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /app/views/checkout.html: -------------------------------------------------------------------------------- 1 |

Checkout

2 | 3 |
4 | 5 |
6 |
7 |

Billing Address

8 |
9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |

Shipping Address

21 | 24 |
25 |
26 |
27 | 28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |

Your Order

41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
ProductQtyTotals
Cart Subtotal{{ cart.totals.formatted.without_tax }}
Order Total{{ cart.totals.formatted.with_tax }}
{{ product.title }}{{ product.quantity }}{{ product.totals.formatted.with_tax }}
69 |
70 |

Shipping

71 |
72 |
73 | 77 |
78 | {{ carrier.description }} 79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 87 |
88 |
89 |

Payment

90 |
91 |
92 |
93 | 97 |
98 |
99 |
100 | 101 | 102 |
103 | -------------------------------------------------------------------------------- /app/views/complete.html: -------------------------------------------------------------------------------- 1 |

Thanks for your order!!!

2 | -------------------------------------------------------------------------------- /app/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |

Storefront

3 |

4 | Put something awesome here...or this could be the /store view. 5 |

6 |
7 | -------------------------------------------------------------------------------- /app/views/payment.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Payment

4 |
5 |
6 | 7 |
8 |
9 |
10 |

Billing Address

11 |
    12 |
  • {{ order.bill_to.data.first_name }} {{ order.bill_to.data.last_name }}
  • 13 |
  • {{ order.bill_to.data.company }}
  • 14 |
  • {{ order.bill_to.data.address_1 }}
  • 15 |
  • {{ order.bill_to.data.address_2 }}
  • 16 |
  • {{ order.bill_to.data.county }}
  • 17 |
  • {{ order.bill_to.data.postcode }}
  • 18 |
  • {{ order.bill_to.data.country.value }}
  • 19 |
20 |
21 |
22 |
23 |
24 |

Shipping Address

25 |
    26 |
  • {{ order.ship_to.data.first_name }} {{ order.ship_to.data.last_name }}
  • 27 |
  • {{ order.ship_to.data.company }}
  • 28 |
  • {{ order.ship_to.data.address_1 }}
  • 29 |
  • {{ order.ship_to.data.address_2 }}
  • 30 |
  • {{ order.ship_to.data.county }}
  • 31 |
  • {{ order.ship_to.data.postcode }}
  • 32 |
  • {{ order.ship_to.data.country.value }}
  • 33 |
34 |
35 |
36 |
37 |
38 |

Payment

39 |
40 | 41 |
42 | 43 |
44 |
45 |
46 | 47 |
48 | 49 |
50 |
51 | 52 |
53 |
54 |
55 | 56 |
57 | 58 |
59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 |
68 | -------------------------------------------------------------------------------- /app/views/product.html: -------------------------------------------------------------------------------- 1 |

{{ product.title }}

2 | 3 |
4 |
5 | 6 | {{product.title}} 7 | 8 |
9 |
10 | {{ product.pricing.formatted.without_tax }} 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /app/views/store.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 |
9 |
10 |

Featured Items

11 | 19 |
20 |
21 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storefront", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": "^1.3.0", 6 | "bootstrap-sass-official": "^3.2.0", 7 | "angular-animate": "^1.3.0", 8 | "angular-cookies": "^1.3.0", 9 | "angular-resource": "^1.3.0", 10 | "angular-route": "^1.3.0", 11 | "angular-sanitize": "^1.3.0", 12 | "angular-touch": "^1.3.0" 13 | }, 14 | "devDependencies": { 15 | "angular-mocks": "^1.3.0" 16 | }, 17 | "appPath": "app", 18 | "moduleName": "storefrontApp" 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storefront", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "repository": {}, 6 | "devDependencies": { 7 | "grunt": "^0.4.5", 8 | "grunt-autoprefixer": "^2.0.0", 9 | "grunt-concurrent": "^1.0.0", 10 | "grunt-contrib-clean": "^0.6.0", 11 | "grunt-contrib-compass": "^1.0.0", 12 | "grunt-contrib-concat": "^0.5.0", 13 | "grunt-contrib-connect": "^0.9.0", 14 | "grunt-contrib-copy": "^0.7.0", 15 | "grunt-contrib-cssmin": "^0.12.0", 16 | "grunt-contrib-htmlmin": "^0.4.0", 17 | "grunt-contrib-imagemin": "^0.9.2", 18 | "grunt-contrib-jshint": "^0.11.0", 19 | "grunt-contrib-uglify": "^0.7.0", 20 | "grunt-contrib-watch": "^0.6.1", 21 | "grunt-filerev": "^2.1.2", 22 | "grunt-google-cdn": "^0.4.3", 23 | "grunt-newer": "^1.1.0", 24 | "grunt-ng-annotate": "^0.9.2", 25 | "grunt-svgmin": "^2.0.0", 26 | "grunt-usemin": "^3.0.0", 27 | "grunt-wiredep": "^2.0.0", 28 | "jshint-stylish": "^1.0.0", 29 | "load-grunt-tasks": "^3.1.0", 30 | "time-grunt": "^1.0.0" 31 | }, 32 | "engines": { 33 | "node": ">=0.10.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "jasmine": true, 22 | "globals": { 23 | "angular": false, 24 | "browser": false, 25 | "inject": false 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /test/spec/controllers/cart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: CartCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var CartCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | CartCtrl = $controller('CartCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/category.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: CategoryCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var CategoryCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | CategoryCtrl = $controller('CategoryCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/checkout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: CheckoutCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var CheckoutCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | CheckoutCtrl = $controller('CheckoutCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/complete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: CompleteCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var CompleteCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | CompleteCtrl = $controller('CompleteCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/payment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: PaymentCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var PaymentCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | PaymentCtrl = $controller('PaymentCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: ProductCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var ProductCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | ProductCtrl = $controller('ProductCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/controllers/store.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: StoreCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('storefrontApp')); 7 | 8 | var StoreCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | StoreCtrl = $controller('StoreCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | --------------------------------------------------------------------------------