├── .gitignore ├── README.md ├── favicon.png ├── fonts ├── FontAwesome.otf ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── gulpfile.js ├── images ├── backend │ ├── advanced │ │ ├── generating-short-hashes-1.png │ │ └── nodejs-error-handling-1.png │ ├── beginner │ │ ├── formatting-dates-1.png │ │ ├── handling-submitted-form-fields-1.png │ │ ├── logging-1.png │ │ ├── logging-2.png │ │ ├── logging-3.png │ │ ├── parsing-query-string-post-and-url-parameters-1.png │ │ ├── parsing-xml-1.png │ │ ├── parsing-xml-2.png │ │ ├── socketio-basics-1.png │ │ ├── useful-nodejs-utilities-1.png │ │ └── using-sass-and-less-in-express.png │ └── intermediate │ │ ├── building-a-rest-api-1.png │ │ ├── csrf-protection-with-express-1.png │ │ ├── csrf-protection-with-express-2.png │ │ ├── csrf-protection-with-express-3.png │ │ ├── form-validation-1.png │ │ ├── form-validation-2.png │ │ ├── get-users-location-from-ip-address-1.png │ │ ├── gravatar-profile-images-1.png │ │ ├── optimizing-assets-with-gulp-1.png │ │ ├── optimizing-assets-with-gulp-2.png │ │ ├── sending-emails-with-nodemailer-1.png │ │ ├── sign-in-with-facebook-1.png │ │ ├── sign-in-with-facebook-2.png │ │ ├── sign-in-with-facebook-3.png │ │ ├── sign-in-with-facebook-4.png │ │ ├── sign-in-with-facebook-5.png │ │ ├── sign-in-with-facebook-6.png │ │ ├── sign-in-with-facebook-7.png │ │ ├── uploading-files-1.png │ │ ├── uploading-files-2.png │ │ ├── uploading-files-3.png │ │ ├── who-is-online-with-socketio-1.png │ │ └── who-is-online-with-socketio-2.png ├── frontend │ ├── advanced │ │ ├── organizing-code-with-requirejs-1.png │ │ └── organizing-code-with-requirejs-2.png │ ├── beginner │ │ └── pinterest-grid-layout-1.png │ └── intermediate │ │ └── star-rating-plugin-1.png └── general │ ├── webstorm-ide-tips-1.png │ ├── webstorm-ide-tips-2.png │ ├── webstorm-ide-tips-3.png │ └── webstorm-ide-tips-4.png ├── index.html ├── package.json ├── posts ├── backend │ ├── advanced │ │ ├── generating-short-hashes.md │ │ ├── nodejs-error-handling.md │ │ └── organizing-callbacks-with-async.md │ ├── beginner │ │ ├── formatting-dates.md │ │ ├── getting-started-with-mongoose.md │ │ ├── handling-submitted-form-fields.md │ │ ├── introduction-to-jade.md │ │ ├── nodejs-logging.md │ │ ├── parsing-query-string-post-and-url-parameters.md │ │ ├── parsing-xml.md │ │ ├── socketio-basics.md │ │ ├── string-manipulation.md │ │ ├── useful-nodejs-utilities.md │ │ └── using-sass-and-less-in-express.md │ └── intermediate │ │ ├── building-a-rest-api.md │ │ ├── csrf-protection-with-express.md │ │ ├── form-validation.md │ │ ├── get-users-location-from-ip-address.md │ │ ├── gravatar-profile-images.md │ │ ├── optimizing-assets-with-gulp.md │ │ ├── sending-emails-with-nodemailer.md │ │ ├── sign-in-with-facebook.md │ │ ├── uploading-files.md │ │ └── who-is-online-with-socketio.md ├── frontend │ ├── advanced │ │ ├── organizing-code-with-requirejs.md │ │ ├── resizable-split-pane-layout.md │ │ └── tables-with-remote-data.md │ ├── beginner │ │ ├── activate-bootstrap-dropdown-on-hover.md │ │ ├── bootstrap-sidebar-menu.md │ │ ├── color-selector.md │ │ ├── comparing-icon-fonts.md │ │ ├── image-carousel-with-slick.md │ │ ├── loading-progress-bar.md │ │ ├── pinterest-grid-layout.md │ │ └── typeahead.md │ └── intermediate │ │ ├── handling-keyboard-shortcuts-in-javascript.md │ │ ├── infinite-scrolling.md │ │ ├── instant-page-load-with-instantclick.md │ │ ├── offline-status-notification.md │ │ ├── parallax-effect.md │ │ ├── search-filter-sort-list-or-tables.md │ │ └── star-rating-plugin.md └── general │ ├── coding-like-a-pro-with-emmet.md │ ├── development-workflow.md │ ├── javascript-style-guide.md │ └── webstorm-ide-tips.md ├── robots.txt ├── scripts ├── app.js ├── compiled.js ├── constants │ └── posts.js ├── controllers │ └── main.js ├── directives │ ├── markdown.js │ └── scroll.js ├── lib │ ├── angular-disqus.min.js │ ├── angular-route.min.js │ ├── angular.min.js │ ├── bootstrap.min.js │ ├── highlight.min.js │ ├── jquery-2.1.0.min.js │ ├── ng-table.min.js │ ├── ngProgress.min.js │ └── showdown.min.js └── services │ ├── github.js │ └── post.js ├── styles ├── lib │ ├── _xcode.scss │ ├── bootstrap │ │ ├── _alerts.scss │ │ ├── _badges.scss │ │ ├── _breadcrumbs.scss │ │ ├── _button-groups.scss │ │ ├── _buttons.scss │ │ ├── _carousel.scss │ │ ├── _close.scss │ │ ├── _code.scss │ │ ├── _component-animations.scss │ │ ├── _dropdowns.scss │ │ ├── _forms.scss │ │ ├── _grid.scss │ │ ├── _input-groups.scss │ │ ├── _jumbotron.scss │ │ ├── _labels.scss │ │ ├── _list-group.scss │ │ ├── _media.scss │ │ ├── _mixins.scss │ │ ├── _modals.scss │ │ ├── _navbar.scss │ │ ├── _navs.scss │ │ ├── _normalize.scss │ │ ├── _pager.scss │ │ ├── _pagination.scss │ │ ├── _panels.scss │ │ ├── _popovers.scss │ │ ├── _print.scss │ │ ├── _progress-bars.scss │ │ ├── _responsive-utilities.scss │ │ ├── _scaffolding.scss │ │ ├── _tables.scss │ │ ├── _theme.scss │ │ ├── _thumbnails.scss │ │ ├── _tooltip.scss │ │ ├── _type.scss │ │ ├── _utilities.scss │ │ ├── _variables.scss │ │ ├── _wells.scss │ │ └── bootstrap.scss │ └── font-awesome │ │ ├── _bordered-pulled.scss │ │ ├── _core.scss │ │ ├── _fixed-width.scss │ │ ├── _icons.scss │ │ ├── _larger.scss │ │ ├── _list.scss │ │ ├── _mixins.scss │ │ ├── _path.scss │ │ ├── _rotated-flipped.scss │ │ ├── _spinning.scss │ │ ├── _stacked.scss │ │ ├── _variables.scss │ │ └── font-awesome.scss ├── styles.css └── styles.scss └── views ├── 404.html ├── contribute.html ├── detail.html ├── feedback.html └── main.html /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | .DS_Store 3 | node_modules 4 | dist 5 | .idea 6 | *.iml 7 | .tmp 8 | .sass-cache 9 | app/bower_components 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## Built Using 5 | - Angular.JS 6 | - Gulp 7 | - Bootstrap Sass 8 | - jQuery 9 | - Showdown 10 | - angular-route 11 | - angular-disqus 12 | - ngProgress 13 | - highlight.js 14 | 15 | ## License 16 | The MIT License (MIT) 17 | 18 | Copyright (c) 2014 Sahat Yalkabov 19 | 20 | Permission is hereby granted, free of charge, to any person obtaining a copy 21 | of this software and associated documentation files (the "Software"), to deal 22 | in the Software without restriction, including without limitation the rights 23 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | copies of the Software, and to permit persons to whom the Software is 25 | furnished to do so, subject to the following conditions: 26 | 27 | The above copyright notice and this permission notice shall be included in all 28 | copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36 | SOFTWARE. 37 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/favicon.png -------------------------------------------------------------------------------- /fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var sass = require('gulp-sass'); 3 | var csso = require('gulp-csso'); 4 | var uglify = require('gulp-uglify'); 5 | var concat = require('gulp-concat'); 6 | 7 | gulp.task('sass', function() { 8 | gulp.src('./styles/styles.scss') 9 | .pipe(sass()) 10 | .pipe(csso()) 11 | .pipe(gulp.dest('styles')); 12 | }); 13 | 14 | gulp.task('compress', function() { 15 | gulp.src([ 16 | 'scripts/lib/jquery-2.1.0.min.js', 17 | 'scripts/lib/angular.min.js', 18 | 'scripts/lib/*.js', 19 | 'scripts/app.js', 20 | 'scripts/constants/*.js', 21 | 'scripts/controllers/*.js', 22 | 'scripts/directives/*.js', 23 | 'scripts/services/*.js' 24 | ]) 25 | .pipe(concat('compiled.js')) 26 | .pipe(uglify()) 27 | .pipe(gulp.dest('scripts')); 28 | }); 29 | 30 | gulp.task('watch', function() { 31 | gulp.watch('styles/*.scss', ['sass']); 32 | gulp.watch(['scripts/**/*.js', '!scripts/compiled.js'], ['compress']); 33 | }); 34 | 35 | gulp.task('default', ['sass', 'compress', 'watch']); 36 | gulp.task('build', ['compress']); -------------------------------------------------------------------------------- /images/backend/advanced/generating-short-hashes-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/advanced/generating-short-hashes-1.png -------------------------------------------------------------------------------- /images/backend/advanced/nodejs-error-handling-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/advanced/nodejs-error-handling-1.png -------------------------------------------------------------------------------- /images/backend/beginner/formatting-dates-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/formatting-dates-1.png -------------------------------------------------------------------------------- /images/backend/beginner/handling-submitted-form-fields-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/handling-submitted-form-fields-1.png -------------------------------------------------------------------------------- /images/backend/beginner/logging-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/logging-1.png -------------------------------------------------------------------------------- /images/backend/beginner/logging-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/logging-2.png -------------------------------------------------------------------------------- /images/backend/beginner/logging-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/logging-3.png -------------------------------------------------------------------------------- /images/backend/beginner/parsing-query-string-post-and-url-parameters-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/parsing-query-string-post-and-url-parameters-1.png -------------------------------------------------------------------------------- /images/backend/beginner/parsing-xml-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/parsing-xml-1.png -------------------------------------------------------------------------------- /images/backend/beginner/parsing-xml-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/parsing-xml-2.png -------------------------------------------------------------------------------- /images/backend/beginner/socketio-basics-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/socketio-basics-1.png -------------------------------------------------------------------------------- /images/backend/beginner/useful-nodejs-utilities-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/useful-nodejs-utilities-1.png -------------------------------------------------------------------------------- /images/backend/beginner/using-sass-and-less-in-express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/beginner/using-sass-and-less-in-express.png -------------------------------------------------------------------------------- /images/backend/intermediate/building-a-rest-api-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/building-a-rest-api-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/csrf-protection-with-express-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/csrf-protection-with-express-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/csrf-protection-with-express-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/csrf-protection-with-express-2.png -------------------------------------------------------------------------------- /images/backend/intermediate/csrf-protection-with-express-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/csrf-protection-with-express-3.png -------------------------------------------------------------------------------- /images/backend/intermediate/form-validation-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/form-validation-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/form-validation-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/form-validation-2.png -------------------------------------------------------------------------------- /images/backend/intermediate/get-users-location-from-ip-address-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/get-users-location-from-ip-address-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/gravatar-profile-images-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/gravatar-profile-images-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/optimizing-assets-with-gulp-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/optimizing-assets-with-gulp-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/optimizing-assets-with-gulp-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/optimizing-assets-with-gulp-2.png -------------------------------------------------------------------------------- /images/backend/intermediate/sending-emails-with-nodemailer-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sending-emails-with-nodemailer-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-2.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-3.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-4.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-5.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-6.png -------------------------------------------------------------------------------- /images/backend/intermediate/sign-in-with-facebook-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/sign-in-with-facebook-7.png -------------------------------------------------------------------------------- /images/backend/intermediate/uploading-files-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/uploading-files-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/uploading-files-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/uploading-files-2.png -------------------------------------------------------------------------------- /images/backend/intermediate/uploading-files-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/uploading-files-3.png -------------------------------------------------------------------------------- /images/backend/intermediate/who-is-online-with-socketio-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/who-is-online-with-socketio-1.png -------------------------------------------------------------------------------- /images/backend/intermediate/who-is-online-with-socketio-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/backend/intermediate/who-is-online-with-socketio-2.png -------------------------------------------------------------------------------- /images/frontend/advanced/organizing-code-with-requirejs-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/frontend/advanced/organizing-code-with-requirejs-1.png -------------------------------------------------------------------------------- /images/frontend/advanced/organizing-code-with-requirejs-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/frontend/advanced/organizing-code-with-requirejs-2.png -------------------------------------------------------------------------------- /images/frontend/beginner/pinterest-grid-layout-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/frontend/beginner/pinterest-grid-layout-1.png -------------------------------------------------------------------------------- /images/frontend/intermediate/star-rating-plugin-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/frontend/intermediate/star-rating-plugin-1.png -------------------------------------------------------------------------------- /images/general/webstorm-ide-tips-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/general/webstorm-ide-tips-1.png -------------------------------------------------------------------------------- /images/general/webstorm-ide-tips-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/general/webstorm-ide-tips-2.png -------------------------------------------------------------------------------- /images/general/webstorm-ide-tips-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/general/webstorm-ide-tips-3.png -------------------------------------------------------------------------------- /images/general/webstorm-ide-tips-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/images/general/webstorm-ide-tips-4.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JS Recipes 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 40 | 41 |
42 |
43 | 44 | 56 | 57 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsrecipes", 3 | "version": "0.0.0", 4 | "devDependencies": { 5 | "gulp": "~3.5.5", 6 | "gulp-concat": "^2.2.0", 7 | "gulp-csso": "^0.2.8", 8 | "gulp-sass": "~0.7.1", 9 | "gulp-uglify": "~0.2.1" 10 | }, 11 | "engines": { 12 | "node": ">=0.10.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /posts/backend/advanced/generating-short-hashes.md: -------------------------------------------------------------------------------- 1 |
2 |

When would I use this?

3 | You need to generate short URLs for something like password reset links, 4 | invitation codes, or perhaps you want to shorten a URL for easy sharing. 5 |
6 | 7 | [Hashids](http://www.hashids.org/) is a Node.js library for generating short, unique, 8 | alphanumeric hashes. Besides the use cases described above, generating short 9 | hashes is also useful for obfuscating user IDs. It could present potential security issues 10 | if you are displaying actual ID (SQL) or [ObjectId](http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html) (MongoDB) 11 | in your app that is visible to the public. 12 | 13 | Here is what I mean. This is one of my older projects where I used ObjectId of 14 | a file as part of the URL. That highlighted alphanumeric string is the actual 15 | ObjectId of this particular document in MongoDB. I needed a unique way to 16 | identify each file and since I could have multiple files with the same name, 17 | ObjectId was used as a unique identifier. Back then I didn't know about *hashids* 18 | library to generate short hashes. 19 | 20 | ![](images/backend/advanced/generating-short-hashes-1.png) 21 | 22 | Hashids has a very simple API, so there is really not much to learn. 23 | 24 | **Install** 25 | ``` 26 | npm install hashids 27 | ``` 28 | As of version *0.3*, hashids includes support for MongoDB ObjectIds, or any hex 29 | string in general. 30 | 31 | **Encryption** 32 | ```javascript 33 | var Hashids = require('hashids'); 34 | 35 | var hashids = new Hashids(); 36 | 37 | // Encrypt integer 38 | var hash = hashids.encrypt(3912); // 'xGqP' 39 | 40 | // Encrypt hex string 41 | var hash = hashids.encryptHex('526b32409e12c3cc1c000002'); // '4rJwNNY42AIOjvqlk5mn' 42 | ``` 43 | 44 | **Decryption** 45 | ```javascript 46 | var Hashids = require('hashids'); 47 | 48 | var hashids = new Hashids(); 49 | 50 | var hash = 'xGqP'; 51 | var id = hashids.decrypt(hash); // [ 3912 ] 52 | 53 | var hash = '4rJwNNY42AIOjvqlk5mn'; 54 | var objectId = hashids.decryptHex(hash); // '526b32409e12c3cc1c000002' 55 | ``` 56 | 57 | **Custom Salt** 58 | 59 | If you don't want other people to decrypt your hashes, provide a unique string 60 | to the `Hashids` constructor. 61 | 62 | ```javascript 63 | var Hashids = require('hashids'); 64 | 65 | var Hashids = new Hashids('unique string that no one could guess'); 66 | ``` 67 | 68 | **Note:** Salt is a random string that is appended to the hashes to make it even 69 | harder to crack it. Here is a really nice [article](https://crackstation.net/hashing-security.htm) 70 | that explains what is a cryptographic salt in greater detail. 71 | 72 | There are a few other things that Hashids can you which you can learn more on 73 | the [GitHub Project](https://github.com/ivanakimov/hashids.node.js) page or go 74 | to the [official page](http://www.hashids.org/node-js/) and scroll down until 75 | you see **full documentation** button, which unfortunately is not linkable. -------------------------------------------------------------------------------- /posts/backend/advanced/nodejs-error-handling.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Error handling is a highly debatable topic in Node.js community because there 4 | is no single true way to do it. Working with Node.js for over 2 years I can 5 | say that error handling is one of the most annoying problems to deal with when 6 | building Node.js web apps. If you are not careful, a single unhandled error 7 | can bring down your entire application, leaving it in the crashed state. 8 | 9 | What's even worse, sometimes it's not easy to find the source of unhandled 10 | exceptions due to cryptic and unhelpful stack traces. In other words, you 11 | could ocassionaly see your app crash, but have no idea what causes it. It could 12 | be a bug in your code or a certain bug that only occurs on a specific 13 | Node.js version on a particular operating system. 14 | 15 | There are primarily three schools of thoughts on error handling: 16 | 17 | - Let the application crash and restart it. 18 | - Handle all possible errors and never crash. 19 | - Balanced approach between the two. 20 | 21 | #### Approach 1: Handle errors in routes. 22 | You are probably aware that most Node.js modules, as a general rule, have an 23 | error object as the first argument in a callback. Unless you properly 24 | handle that error, your app will likely crash sooner or later. 25 | 26 | Look at this code below. It simply means if an error is raised for whatever 27 | reason, the `if (err)` line will intercept it and do something with it, in this case 28 | `throw` it. In other words your application will still crash. 29 | 30 | ```javascript 31 | app.get('/', function(req, res) { 32 | Item.find(function(err, items) { 33 | if (err) throw err; 34 | console.log(items); 35 | }); 36 | }); 37 | ``` 38 | 39 | The difficult part of handling errors is you have to decide what 40 | do you want to do when an error occurs? What do you think should happen if we 41 | can't retrieve our items from database? There is no correct answer, it will 42 | ultimately depend on your application. Personally, I don't believe that we 43 | should crash and restart the server for errors like these. 44 | 45 | A better approach would be to display a flash notifications with 46 | "Items could not be retrieved from database. Please try again" or if you 47 | don't want to do flash notificans you could simply display a blank page with 48 | that same text: 49 | 50 | ```javascript 51 | app.get('/', function(req, res) { 52 | Item.find(function(err, items) { 53 | if (err) { 54 | return res.send('Items could not be retrieved from database. Please try again.'); 55 | } 56 | console.log(items); 57 | }); 58 | }); 59 | ``` 60 | 61 | ![](images/backend/advanced/nodejs-error-handling-1.png) 62 | 63 | To me that's a better approach than crashing the server and letting it restart 64 | itself via [supervisor](https://github.com/isaacs/node-supervisor) or 65 | [forever](https://github.com/nodejitsu/forever) modules. The reason why I 66 | called this "in-place" error handling is because you are taking care of errors 67 | inside `if (err)` block for each asynchronous operation that returns an error object 68 | as the first argument. In [Hackathon Starter](https://github.com/sahat/hackathon-starter) 69 | `api.js` controller alone, there are over 79 occurences of error objects. 70 | Handling each `err` individually would result in tremendous amount of code 71 | duplication. 72 | 73 | The next best thing you can do is to delegate all error handling logic to an 74 | Express middleware. 75 | 76 | 77 | #### Approach 2: Handle errors in middleware. 78 | 79 | ```javascript 80 | app.get('/', function(req, res, next) { 81 | Item.find(function(err, items) { 82 | if (err) return next(err); 83 | console.log(items); 84 | }); 85 | }); 86 | ``` 87 | 88 | **Note:** It is important to **return** inside `if (err) { ... }` otherwise your 89 | code will just continue executing and eventually crash. 90 | 91 | Two things to note here: 92 | 93 | 1. An extra route parameter `next`. 94 | 2. When error occurs, call `return next()` and pass it an error object. 95 | 96 | Then add this middleware after all other Express middlewares: 97 | 98 | ```javascript 99 | app.use(function(err, req, res, next) { 100 | console.error(err.stack); 101 | return res.send(500, { message: err.message }); 102 | }); 103 | ``` 104 | 105 | This middleware will print out error stack to console and render a JSON page 106 | with the error message. This is just an example code. If you are using some 107 | third-party logging tools, this is where you could trigger error events. It's 108 | totally up to you in deciding what to do with those errors. Or perhaps this 109 | is where you would display a generic custom error page with the error message. 110 | 111 | #### Bonus: Error Handling in Node.js by Jamund Ferguson 112 | 113 | 114 | 115 |
116 | #### Additional Resources 117 | 118 | 1. [Node.js Error Handling Patterns](http://www.nodewiz.biz/nodejs-error-handling-pattern) 119 | 2. [Best Practices for Error Handling in Node.js](http://www.joyent.com/blog/best-practices-for-error-handling-in-node-js) 120 | 3. [Error handling in node.js](http://machadogj.com/2013/4/error-handling-in-nodejs.html) 121 | 4. [Node.js Best Practice Exception Handling](https://gist.github.com/balupton/5560110) 122 | -------------------------------------------------------------------------------- /posts/backend/beginner/handling-submitted-form-fields.md: -------------------------------------------------------------------------------- 1 | Suppose you have the following form and you would like to send user input back 2 | to the server and store it somewhere. 3 | 4 | ``` 5 | form(action='/signup', method='POST') 6 | .row 7 | .col-xs-6.form-group 8 | label First Name 9 | input.form-control(type='text', name='firstname') 10 | .col-xs-6.form-group 11 | label Last Name 12 | input.form-control(type='text', name='lastname') 13 | .form-group 14 | label Email Address 15 | input.form-control(type='email', name='email') 16 | .form-group 17 | label Username 18 | input.form-control(type='text', name='username') 19 | .form-group 20 | label Password 21 | input.form-control(type='password', name='password') 22 | .form-group 23 | input.btn.btn-primary(type='submit', value='Signup') 24 | ``` 25 | 26 | I am using [Jade](http://jade-lang.com/) template engine and 27 | [Bootstrap 3](http://getbootstrap.com) for this example. Here is the screenshot 28 | of how it looks: 29 | 30 | ![](images/backend/beginner/handling-submitted-form-fields-1.png) 31 | 32 | Since this form has a method `POST` and an action `/signup`, you must define 33 | a route to handle form submission. Notice how each *input* field has a **name** 34 | attribute. It is the same name you use in order to access it from your `app.post` route. 35 | 36 | ``` 37 | app.post('/signup', function(req, res) { 38 | var user = new User({ 39 | firstName: req.body.firstname, 40 | lastName: req.body.lastname, 41 | email: req.body.email, 42 | username: req.body.username, 43 | password: req.body.password 44 | }); 45 | 46 | user.save(function(err) { 47 | req.login(user, function(err) { 48 | res.redirect('/'); 49 | }); 50 | }); 51 | }); 52 | ``` 53 | 54 | In the case of radio buttons, keep the same **name** attribute on logically grouped 55 | radio buttons, but different **value** attributes to differentiate them from 56 | each other. 57 | 58 | ``` 59 | label.radio-inline 60 | input(type='radio', name='gender', value='male') 61 | | Male 62 | label.radio-inline 63 | input(type='radio', name='gender', value='female') 64 | | Female 65 | ``` 66 | 67 | This would print *male* or *female* depending on the radio button choice. 68 | 69 | ``` 70 | app.post('/signup', function(req, res, next) { 71 | console.log(req.body.gender); 72 | }); 73 | ``` 74 | 75 | On a related note, this is also how you could also do input validation inside your 76 | route, before you try to create a new user: 77 | 78 | ``` 79 | if (!req.body.username) { 80 | return res.send(400, 'Username cannot be blank.'); 81 | } 82 | 83 | if (!req.body.email) { 84 | return res.send(400, 'Email cannot be blank.'); 85 | } 86 | 87 | if (!req.body.password) { 88 | return res.send(400, 'Password cannot be blank.'); 89 | } 90 | ``` 91 | 92 | **Note**: A better way would be to use [validator.js](http://validatorjs.org/) or 93 | [express-validator](https://github.com/ctavan/express-validator) libraries, then redirect 94 | to the same page and display flash messages. Try submitting a blank 95 | form on [Hackathon Starter](http://hackathonstarter.herokuapp.com/signup) to see 96 | what I mean. But that is a discussion for another article. 97 | 98 | To recap, you create a form with `method='POST'` and `action='/url'` with whatever URL that you 99 | are *POST*'ing that form to. Make sure each input field has a unique **name** attribute. 100 | Then to access that input value from your `app.post` route your would use 101 | `req.body.INPUT_NAME_ATTRIBUTE`. 102 | 103 |
104 | #### Additional Resources 105 | 106 | 1. [Express API Reference on req.body](http://expressjs.com/3x/api.html#req.body) -------------------------------------------------------------------------------- /posts/backend/beginner/nodejs-logging.md: -------------------------------------------------------------------------------- 1 | You may be used to logging things to console using `console.log`. It generally 2 | works fine, but there is a better way! Take a look at how **npm** does it: 3 | 4 | ![](images/backend/beginner/logging-1.png) 5 | 6 | Using `npmlog` you could use the same nice logs in your app. Use `npm install` 7 | to get the library and then add this to your module declarations: 8 | 9 | ```js 10 | var log = require('npmlog'); 11 | ``` 12 | 13 | Now you can use `log.info` instead of `console.log` and `log.error` instead of 14 | `console.error`, to print out console messages. 15 | 16 | ```js 17 | app.listen(app.get('port'), function() { 18 | log.info('Express', 'Listening on port %s', app.get('port')); 19 | }); 20 | ``` 21 | 22 | **Note**: You could also omit the first string parameter if you 23 | prefer not to display the prefix. 24 | 25 | ![](images/backend/beginner/logging-2.png) 26 | 27 | More generally here is the complete structure of `npmlog`: 28 | 29 | ```js 30 | // additional stuff ---------------------------+ 31 | // message ----------+ | 32 | // prefix ----+ | | 33 | // level -+ | | | 34 | // v v v v 35 | log.info('fyi', 'I have a kitty cat: %j', myKittyCat) 36 | ``` 37 | 38 | And just for completeness, here is an example using `log.error` to log an 39 | error message: 40 | 41 | ```js 42 | mongoose.connect('localhost'); 43 | mongoose.connection.on('error', function(err) { 44 | log.error(err); 45 | }); 46 | ``` 47 | 48 | ![](images/backend/beginner/logging-3.png) 49 | 50 | To learn more about **npmlog**, visit the [GitHub Project](https://github.com/npm/npmlog). 51 | -------------------------------------------------------------------------------- /posts/backend/beginner/parsing-query-string-post-and-url-parameters.md: -------------------------------------------------------------------------------- 1 | There are three common ways of sending data from client to server: 2 | 3 | 1. Query String - key-value pair after `?`, separated by `&`. 4 | 2. POST - parameters that are sent as part of a `POST` request. 5 | 3. URL - route parameters, e.g. `/posts/:id`. 6 | 7 | ### Query String 8 | 9 | The following route returns the query that you have typed: 10 | 11 | ``` 12 | // GET /search?q=hello+world 13 | app.get('/search', function(req, res) { 14 | res.send('Your query: '+ req.query.q + '\n'); 15 | }); 16 | ``` 17 | Here is a screenshot for demonstration purposes: 18 | 19 | ![](images/backend/beginner/parsing-query-string-post-and-url-parameters-1.png) 20 | 21 | Basically, whichever key name you use in the URL after the `?`, you should use 22 | the same name in your Express routes: `req.query.KEY_NAME`. 23 | 24 | Similarly, you can use multiple different keys or the same key with multiple 25 | values: 26 | 27 | ``` 28 | // GET /books?order=desc&book[genre]=fiction&book[author]=richelle+mead 29 | req.query.order // desc 30 | req.query.book.genre // fiction 31 | req.query.book.author // richelle mead 32 | ``` 33 | 34 | Here is another example using the same key multiple times: 35 | 36 | ``` 37 | // GET /filter?stores=zara&stores=macys&stores=uniqlo 38 | req.query.stores // ['zara', 'macys', 'uniqlo'] 39 | ``` 40 | 41 | That's all there is to it. I should also point out that there is a built-in 42 | [Query String](http://nodejs.org/api/querystring.html) module that might of 43 | interest. 44 | 45 | ### POST 46 | 47 | There is already an in-depth post written [here](#/backend/handling-submitted-form-fields). 48 | Examples on that page use HTML Form for submitting POST data. In this example I will 49 | use jQuery AJAX to send a POST request. 50 | 51 | **Server** 52 | ``` 53 | app.post('/signup', function(req, res) { 54 | var user = new User({ 55 | firstName: req.body.firstname, 56 | lastName: req.body.lastname, 57 | email: req.body.email, 58 | username: req.body.username, 59 | password: req.body.password 60 | }); 61 | 62 | user.save(function(err) { 63 | res.send('Successfully created new user'); 64 | }); 65 | }); 66 | ``` 67 | 68 | **Client** 69 | ``` 70 | var user = { 71 | firstName: 'Kallen', 72 | lastName: 'Stadtfeld', 73 | email: 'kallen@ashford.edu', 74 | username: 'kallen', 75 | password: 'password' 76 | }; 77 | 78 | $.ajax({ 79 | type: 'POST', 80 | url: '/signup', 81 | data: user, 82 | success: function(data) { 83 | console.log(data); // "Successfully created new user" 84 | }); 85 | }); 86 | ``` 87 | 88 | As you can see it doesn't matter how you send the data, as long as it is a POST 89 | request, you will always use `req.body` to retrieve it in Express. 90 | 91 | ### URL 92 | 93 | Suppose you have the following route, you can access `:name` parameter via 94 | `req.params.name`. 95 | 96 | ``` 97 | // GET /user/smith 98 | app.get('/user/:name', function(req, res) { 99 | console.log(req.params.name); // "smith" 100 | }); 101 | ``` 102 | 103 | There are many use cases for URL parameters. One such use case, perhaps the most common, 104 | is demonstrated above, where you want to display a page for a particular user. 105 | 106 | Or perhaps you have a route that deletes a file by id: 107 | 108 | ``` 109 | app.del('/files/:id', function(req, res) { 110 | File.remove({ _id: req.params.id }, function(err) { 111 | console.log('File removed'); 112 | }); 113 | }); 114 | ``` 115 | -------------------------------------------------------------------------------- /posts/backend/beginner/parsing-xml.md: -------------------------------------------------------------------------------- 1 | Many third-party APIs return JSON data these days, but on some occasions 2 | you will have no choice but to work with XML. 3 | 4 | To get started, you will need to install `xml2js` library. For the purposes of 5 | this tutorial you will also need `request` library to make a remote request 6 | for an XML document: 7 | 8 | ``` 9 | npm install xml2js request 10 | ``` 11 | 12 | We will be making a request to EVE Online API to retrieve a Character ID string. 13 | There is only one required parameter - *character name*. Any valid EVE Online character name 14 | would work. 15 | 16 | ![](images/backend/beginner/parsing-xml-1.png) 17 | 18 | Here is the code to parse that XML and print out the Character ID. 19 | ``` 20 | // Module dependencies 21 | var request = require('request'); 22 | var xml2js = require('xml2js'); 23 | 24 | // xml2js parser instance 25 | var parser = new xml2js.Parser(); 26 | 27 | // GET request to EVE Online API 28 | var characterName = 'Nova Kierra'; 29 | var url = 'https://api.eveonline.com/eve/CharacterID.xml.aspx?names=' + characterName; 30 | request.get(url, function(error, request, body) { 31 | // Parse XML data from body 32 | parser.parseString(body, function(err, parsedXml) { 33 | try { 34 | var characterId = parsedXml.eveapi.result[0].rowset[0].row[0].$.characterID; 35 | console.log(characterId); 36 | } catch(e) { 37 | console.log('Character not found'); 38 | } 39 | }); 40 | }); 41 | ``` 42 | 43 | The `parseString` method takes XML input as its first argument, and 44 | returns callback function with a JSON object. Here is the contents 45 | of the `parsedXML` parameter: 46 | 47 | ![](images/backend/beginner/parsing-xml-2.png) 48 | 49 | Notice that there is a try-catch block around `var characterId`. Many times XML structure 50 | will differ depending on what it is returning. If character name is not specified, 51 | it will no longer have the same structure: 52 | 53 | ``` 54 | 55 | 2014-03-24 17:57:04 56 | 57 | General Error: Scotty the docking manager heard you were talking shit about him behind his back and refuses to service your request. 58 | 59 | 2014-03-24 18:57:04 60 | 61 | ``` 62 | 63 | Parsing that XML document using provided code will result in the following exception. And 64 | remember any exception that is thrown will bring down your node.js process! So 65 | be sure to add try-catch blocks around your code whenever you are parsing XML. 66 | 67 | ``` 68 | TypeError: Cannot read property '0' of undefined 69 | at /Users/sahat/xmldemo/app.js:42:54 70 | at Parser. (/Users/sahat/xmldemo/node_modules/xml2js/lib/xml2js.js:368:20) 71 | at Parser.EventEmitter.emit (events.js:95:17) 72 | at Object.saxParser.onclosetag (/Users/sahat/xmldemo/node_modules/xml2js/lib/xml2js.js:338:24) 73 | at emit (/Users/sahat/xmldemo/node_modules/xml2js/node_modules/sax/lib/sax.js:615:33) 74 | at emitNode (/Users/sahat/xmldemo/node_modules/xml2js/node_modules/sax/lib/sax.js:620:3) 75 | at closeTag (/Users/sahat/xmldemo/node_modules/xml2js/node_modules/sax/lib/sax.js:861:5) 76 | at Object.write (/Users/sahat/xmldemo/node_modules/xml2js/node_modules/sax/lib/sax.js:1293:29) 77 | at Parser.exports.Parser.Parser.parseString (/Users/sahat/xmldemo/node_modules/xml2js/lib/xml2js.js:386:29) 78 | at Parser.parseString (/Users/sahat/xmldemo/node_modules/xml2js/lib/xml2js.js:6:61) 79 | ``` 80 | 81 | That's really all there is to parsing XML documents. To learn more about additional XML 82 | parser options, visit the [node-xml2js GitHub Project](https://github.com/Leonidas-from-XIV/node-xml2js#options). 83 | 84 | -------------------------------------------------------------------------------- /posts/backend/beginner/string-manipulation.md: -------------------------------------------------------------------------------- 1 | JavaScript lacks complete string manipulation operations. This an attempt to fill that gap. List of build-in methods can be found for example from Dive Into JavaScript. 2 | 3 | ### Case 4 | 5 | [Case](https://github.com/nbubna/Case) is a string utility library that lets 6 | you perform all sorts of string manipulations. 7 | 8 | The use case of this library will largely depend on the application itself. 9 | In my scenario of the Senior Design project, when I upload a file of this 10 | form - **readQR.cpp**, **cs342-hw1.pdf** or **redis_book.pdf**, I need 11 | to split the filename into individual keywords so that I could store those keywords 12 | in database and later search these files by keywords. For example I could 13 | just type *hw1* and it would return me the file **cs342-hw1.pdf**. 14 | 15 | #### Example Usage 16 | 17 | ```javascript 18 | Case.upper('foo_bar') -> 'FOO BAR' 19 | Case.lower('fooBar') -> 'foo bar' 20 | Case.snake('Foo bar!') -> 'foo_bar' 21 | Case.squish('foo.bar') -> 'FooBar' 22 | Case.camel('foo, bar') -> 'fooBar' 23 | Case.constant('Foo-Bar') -> 'FOO_BAR' 24 | Case.title('foo v. bar') -> 'Foo v. Bar' 25 | Case.capital('foo_v_bar') -> 'Foo V Bar' 26 | Case.sentence('"foo!" said bar', ['Bar']) -> '"Foo!" said Bar' 27 | ``` 28 | 29 | Another useful method is `of`. With it you can actually identify the case of a 30 | given string. When processing uploaded files, you could use this as a first step 31 | in helping you to identify the string case, and only then proceed to string case 32 | manipulations such as *camel*, *snake*, etc. 33 | 34 | ```javascript 35 | Case.of('foo') -> 'lower' 36 | Case.of('foo_bar') -> 'snake' 37 | Case.of('Foo v Bar') -> 'title' 38 | Case.of('foo_ Bar') -> undefined 39 | ``` 40 | 41 | ### underscore.string 42 | 43 | [Underscore.string](https://github.com/epeli/underscore.string) is another popular 44 | library for string manipulation. 45 | 46 | **Note:** As an alternative, take a look at [string.js](http://stringjs.com/). There 47 | is a lot of overlap between the two libraries, but since I only have experience 48 | with underscore.string, that's what I will cover here. 49 | 50 | I will not list every single API function here, only the ones I find really useful 51 | from my experience of building web apps. 52 | 53 | #### String Functions 54 | 55 | **_.clean(str)** Compress some whitespaces to one. 56 | 57 | **Real-world Example:** If you have a textarea that takes user's message, this 58 | would be a great place to strip down unnecessary spaces before, after and 59 | even *between* strings. 60 | 61 | ```javascript 62 | _.clean(" foo bar ") 63 | => 'foo bar' 64 | ``` 65 | 66 | **_.count(string, substring)** Counts the number of substrings in a string. 67 | 68 | ```javascript 69 | _('Hello world').count('o') 70 | => 2 71 | ``` 72 | 73 | **_.humanize(string)** Converts an underscored, camelized, or dasherized string 74 | into a humanized one. Also removes beginning and ending whitespace. 75 | 76 | ```javascript 77 | _(' capitalize dash-CamelCase_underscore trim ').humanize() 78 | => 'Capitalize dash camel case underscore trim' 79 | ``` 80 | 81 | **_.truncate(string, length, truncateString)** Truncate a string after *n* characters. 82 | 83 | ```javascript 84 | _('Hello world').truncate(5) 85 | => 'Hello...' 86 | ``` 87 | 88 | **_.toSentence(array, [delimiter, lastDelimiter])** Join an array into a human readable sentence. 89 | 90 | ```javascript 91 | _.toSentence(['jQuery', 'Mootools', 'Prototype']) 92 | => 'jQuery, Mootools and Prototype'; 93 | 94 | _.toSentence(['jQuery', 'Mootools', 'Prototype'], ', ', ' unt ') 95 | => 'jQuery, Mootools unt Prototype'; 96 | ``` 97 | 98 | **_.surround(string, wrap)** Surround a string with another string. 99 | 100 | ```javascript 101 | _.surround("foo", "ab") 102 | => 'abfooab'; 103 | ``` 104 | 105 | **_.slugify(string)** Transform text into a URL slug. 106 | Replaces whitespaces, accentuated, and special characters with a dash. 107 | 108 | ```javascript 109 | _.slugify("Un éléphant à l'orée du bois") 110 | => 'un-elephant-a-loree-du-bois'; 111 | ``` 112 | **Real-world Example:** Consider this website for example. The post you are 113 | reading right now is titled *String Manipulation*, but I can't use that 114 | string for URL (well, technically I could, but it would look pretty ugly as *String%20Manipulation*). 115 | Instead I use the sluggified version - *string-manipulation*, 116 | which works really nicely in browsers. No matter how weird or complex a title 117 | may be, sluggified string would always be simple and contain only dashes instead 118 | of punctuation and special characters. 119 | 120 | 121 |
122 | #### Additional Resources 123 | 124 | 1. [Full List of String Functions in underscore.string](https://github.com/epeli/underscore.string#string-functions) 125 | -------------------------------------------------------------------------------- /posts/backend/beginner/useful-nodejs-utilities.md: -------------------------------------------------------------------------------- 1 | ### Nodemon 2 | 3 | Nodemon will watch the files in the directory that nodemon was started, 4 | and if they change, it will automatically restart your node application. 5 | 6 | ![](images/backend/beginner/useful-nodejs-utilities-1.png) 7 | 8 | Now, any time you make a change to `app.js` or to its dependent modules (e.g. 9 | controllers, models, routes), nodemon will automatically restart the server, so 10 | you don't have to. I can't recommend this module enough! With nodemon you 11 | no longer have to press **CTRL + C**, **Up Arrow**, **Enter** hundred times a day 12 | to restart the server every single time you make a small change in your app. -------------------------------------------------------------------------------- /posts/backend/intermediate/csrf-protection-with-express.md: -------------------------------------------------------------------------------- 1 | Why should you worry about preventing cross-site request forgery attacks? For toy 2 | or hackathon apps it probably doesn't matter as much, but if you are running a 3 | production website there is no reason not to have CSRF protection. 4 | 5 | Consider the following scenario: a banking web app hasn't been properly 6 | protected against CSRF attacks, so then a malicious hacker could convince 7 | the user to visit another website while logged 8 | in to their banking website. This website could then run a POST request 9 | to transfer money from the victim's account to the attacker's account 10 | without the victim's consent or knowledge. 11 | 12 | In layman terms, with CSRF protection you will have a hidden `` field 13 | on every POST form. That hidden input field has some random hash value, without 14 | which the form will not submit, i.e. if the CSRF token is missing, you will 15 | be greeted with a **403 Forbidden** page when submitting a form. 16 | 17 | ![](images/backend/intermediate/csrf-protection-with-express-1.png) 18 | 19 | And here is what happens when you forget to include the hidden input field with 20 | CSRF value: 21 | 22 | ![](images/backend/intermediate/csrf-protection-with-express-2.png) 23 | 24 | Luckily for us Express already comes with CSRF middleware. The CSRF middleware 25 | depends on `bodyParser` and `session` middleware, so you have to place it below 26 | those two middleware. 27 | 28 | ``` 29 | // all environments 30 | app.set('port', process.env.PORT || 3000); 31 | app.set('views', path.join(__dirname, 'views')); 32 | app.set('view engine', 'jade'); 33 | app.use(express.favicon()); 34 | app.use(express.logger('dev')); 35 | app.use(express.json()); 36 | app.use(express.urlencoded()); 37 | app.use(express.methodOverride()); 38 | app.use(express.cookieParser()); 39 | app.use(express.session({secret: 'your secret'})); 40 | app.use(express.csrf()); 41 | app.use(app.router); 42 | app.use(express.static(path.join(__dirname, 'public'))); 43 | ``` 44 | 45 | **Note:** The `bodyParser` middleware has been deprecated in favor of `express.json` and 46 | `express.urlencoded`. 47 | 48 | Below `express.csrf()`, add the following middleware: 49 | 50 | ``` 51 | app.use(function(req, res, next) { 52 | res.locals._csrf = req.csrfToken(); 53 | next(); 54 | }); 55 | ``` 56 | 57 | **Note:** When implementing authentication, I usually use the middleware above 58 | for storing `res.locals.user = req.user` reference, so that I don't have 59 | to pass `user: req.user` to every template. 60 | 61 | The last thing left to do is to add a hidden input element to your form: 62 | 63 | ``` 64 | input(type='hidden', name='_csrf', value=_csrf) 65 | ``` 66 | 67 | Here is an example of a Login form with the CSRF input tag: 68 | 69 | ```jade 70 | h2 Login Form 71 | 72 | form(method='POST') 73 | input(type='hidden', name='_csrf', value=_csrf) 74 | 75 | .form-group 76 | label.control-label(for='email') Email 77 | input.form-control(type='text', name='email', id='email', placeholder='Email', autofocus=true) 78 | 79 | .form-group 80 | label.control-label(for='password') Password 81 | input.form-control(type='password', name='password', id='password', placeholder='Password') 82 | 83 | .form-group 84 | button.btn.btn-primary(type='submit') Login 85 | ``` 86 | 87 | ![](images/backend/intermediate/csrf-protection-with-express-3.png) 88 | 89 | ### Source Code 90 |
91 | 92 | #### package.json 93 | ```javascript 94 | { 95 | "name": "application-name", 96 | "version": "0.0.1", 97 | "private": true, 98 | "scripts": { 99 | "start": "node app.js" 100 | }, 101 | "dependencies": { 102 | "express": "3.5.1", 103 | "jade": "*" 104 | } 105 | } 106 | ``` 107 | 108 | #### app.js 109 | ```javascript 110 | var express = require('express'); 111 | var path = require('path'); 112 | 113 | var app = express(); 114 | 115 | app.set('port', process.env.PORT || 3000); 116 | app.set('views', path.join(__dirname, 'views')); 117 | app.set('view engine', 'jade'); 118 | app.use(express.favicon()); 119 | app.use(express.logger('dev')); 120 | app.use(express.json()); 121 | app.use(express.urlencoded()); 122 | app.use(express.cookieParser()); 123 | app.use(express.session({ secret: 'session secret' })); 124 | app.use(express.csrf()); 125 | app.use(function(req, res, next) { 126 | res.locals._csrf = req.csrfToken(); 127 | next(); 128 | }); 129 | app.use(express.methodOverride()); 130 | app.use(app.router); 131 | app.use(express.static(path.join(__dirname, 'public'))); 132 | 133 | if ('development' == app.get('env')) { 134 | app.use(express.errorHandler()); 135 | } 136 | 137 | app.get('/', function(req, res) { 138 | res.render('index'); 139 | }); 140 | 141 | app.post('/login', function(req, res) { 142 | res.send('Login Successful') 143 | }); 144 | 145 | app.listen(app.get('port'), function(){ 146 | console.log('Express server listening on port ' + app.get('port')); 147 | }); 148 | ``` 149 | 150 | #### views/layout.jade 151 | 152 | ```jade 153 | doctype html 154 | html 155 | head 156 | title CSRF Demo 157 | link(rel='stylesheet', href='//cdn.jsdelivr.net/foundation/5.2.1/css/foundation.min.css') 158 | link(rel='stylesheet', href='//cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.css') 159 | style. 160 | body { padding-top: 40px; } 161 | body 162 | block content 163 | ``` 164 | 165 | #### views/index.jade 166 | ```jade 167 | extends layout 168 | 169 | block content 170 | .row 171 | .small-8.small-centered.columns 172 | h3 Login Form 173 | form(action='/login', method='POST') 174 | input(type='hidden', name='_csrf', value=_csrf) 175 | label Email 176 | input(type='text', placeholder='Email', autofocus=true) 177 | label Password 178 | input(type='text', placeholder='Password') 179 | button.success.button(type='submit') 180 | i.fi-laptop 181 | | Login 182 | 183 | .panel.callout.radius 184 | strong CSRF Token 185 | p= _csrf 186 | ``` 187 | 188 |
189 | #### Additional Resources 190 | 191 | 1. [DailyJS: Express 3 Tutorial: Contact Forms with CSRF](http://dailyjs.com/2012/09/13/express-3-csrf-tutorial/) 192 | -------------------------------------------------------------------------------- /posts/backend/intermediate/get-users-location-from-ip-address.md: -------------------------------------------------------------------------------- 1 | You already have an ability to get an IP address of your users. Using Express, 2 | this is how you would display someone's IP address. 3 | 4 | ``` 5 | app.get('/', function(req, res) { 6 | res.send('Your IP Address: ' + req.ip); 7 | }); 8 | ``` 9 | 10 | IP address by itself is not very interesting, what would be pretty cool is to 11 | find out the location of that IP address. 12 | 13 | Consider the following scenario. You are building an app using [Foursquare](http://foursquare.com) 14 | API and you need to display [trending venus](https://developer.foursquare.com/docs/venues/trending) 15 | near the user, *without using geolocation*. You know user's IP address, but you 16 | don't know where that user is located. Luckily there is a library for that. 17 | 18 | ![](images/backend/intermediate/get-users-location-from-ip-address-1.png) 19 | 20 | Download and install geoip-lite: 21 | 22 | ``` 23 | npm install --save geoip-lite 24 | ``` 25 | 26 | Here is a basic usage: 27 | 28 | ``` 29 | var geoip = require('geoip-lite'); 30 | 31 | var ip = '207.97.227.239'; 32 | var geo = geoip.lookup(ip); 33 | 34 | console.log(geo); 35 | ``` 36 | 37 | **Output** 38 | ``` 39 | { 40 | range: [ 3479299040, 3479299071 ], 41 | country: 'US', 42 | region: 'CA', 43 | city: 'San Francisco', 44 | ll: [37.7484, -122.4156] 45 | } 46 | ``` 47 | 48 | You give it an IP address and it returns JSON object with **country**, **region**, 49 | **city** attributes, as well as **latitute/longitude** coordinates. In our 50 | scenario with Foursquare API latitude and longitude coordinates is all you 51 | actually need to list trending venues near that location. 52 | 53 |
54 | #### Additional Resources 55 | 56 | 1. [GeoIP-lite GitHub Project](https://github.com/bluesmoon/node-geoip) 57 | -------------------------------------------------------------------------------- /posts/backend/intermediate/gravatar-profile-images.md: -------------------------------------------------------------------------------- 1 | Websites like GitHub and [StackOverflow](http://meta.stackoverflow.com/questions/47991/how-is-gravatar-set-on-stack-overflow) 2 | use [Gravatar](http://en.gravatar.com/) 3 | service to display profile images. Whether you want to use Gravatar images or custom 4 | profile images uploaded by users instead will depends on your application's use case. 5 | 6 | Getting profile images from Gravatar is actually simpler than you might think. 7 | All you need is a built-in Node.js `crypto` library and the Gravatar URL that you 8 | can find at [Gravatar Developer Resources](https://en.gravatar.com/site/implement/). 9 | 10 | ``` 11 | var crypto = require('crypto'); 12 | 13 | var email = 'sahat@msn.com'; 14 | var md5 = crypto.createHash('md5').update(email).digest('hex'); 15 | var url = 'http://www.gravatar.com/avatar/' + md5; 16 | ``` 17 | 18 | That's the simplest example of Gravatar usage. That URL above returns a 19 | profile image of 80x80 in size. Basically you query Gravatar by your e-mail, 20 | except you are not using your actual email, but a hashed version of your e-mail. 21 | 22 | 23 | 24 | 25 | You can also specify custom image size anywhere from 1px to 2048px by passing 26 | an extra query string parameter: 27 | 28 | ``` 29 | var large = 'http://www.gravatar.com/avatar/' + md5 + '?s=250'; 30 | ``` 31 | If specified e-mail has no matching Gravatar image you will the following image: 32 | 33 | 34 | 35 | So, what is this MD5 hash anyway? From [MD5 Hash Generator](http://www.md5hashgenerator.com): 36 | > An MD5 hash is created by taking a string of an any length and encoding it into a 128-bit fingerprint. Encoding the same string using the MD5 algorithm will always result in the same 128-bit hash output. This tool provides a quick and easy way to encode an MD5 hash from a simple string of up to 256 characters in length. 37 | 38 | ![](images/backend/intermediate/gravatar-profile-images-1.png) 39 | 40 |
41 |

Mongoose Integration

42 | You can use the following helper method to simplify the process of 43 | retrieve Gravatar images. 44 |
45 | 46 | Add the following instance method to your User schema. 47 | 48 | ``` 49 | userSchema.methods.gravatar = function(size, defaults) { 50 | if (!size) size = 200; 51 | if (!defaults) defaults = 'retro'; 52 | 53 | if (!this.email) { 54 | return 'https://gravatar.com/avatar/?s=' + size + '&d=' + defaults; 55 | } 56 | 57 | var md5 = crypto.createHash('md5').update(this.email); 58 | return 'https://gravatar.com/avatar/' + md5.digest('hex').toString() + '?s=' + size + '&d=' + defaults; 59 | }; 60 | ``` 61 | 62 | Then inside your template this is how you would display a Gravatar image. It will 63 | use currently logged-in user's e-mail address to generate a Gravatar URL on the fly. 64 | 65 | ``` 66 | img(src='#{user.gravatar()}') 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /posts/backend/intermediate/sending-emails-with-nodemailer.md: -------------------------------------------------------------------------------- 1 | Sending e-mails is such a common task and yet it is not immediately obvious how 2 | to do it. When I was first learning how to send an e-mail using Node.js, I had no 3 | idea where to start. My concept of an email server was associated with Microsoft 4 | Exchange Server. Believe it or not, I actually thought I needed to have 5 | something like Microsoft Exchange Server running somewhere on the cloud to send 6 | a simple e-mail message! 7 | 8 | Fortunately sending e-mails is very easy in Node.js. We will be using [Nodemailer](https://github.com/andris9/Nodemailer) 9 | library. It supports both plain text and HTML emails. The best part about 10 | Nodemailer is that it supports so many services: **Gmail**, **Hotmail**, **Yahoo**, 11 | **SendGrid**, **Mailgun**, **iCloud** and many more. You can even send e-mails 12 | directly without using any third-party services, although there is a good chance 13 | it will end up in user's **Spam** folder if you are sending an e-mail directly. 14 | 15 | To get started, download and install Nodemailer: 16 | 17 | ``` 18 | npm install --save nodemailer 19 | ``` 20 | 21 | In your **app.js** add the following: 22 | 23 | ``` 24 | var nodemailer = require('nodemailer'); 25 | 26 | var smtpTransport = nodemailer.createTransport('SMTP', { 27 | service: 'Gmail', 28 | auth: { 29 | user: 'username@gmail.com', 30 | pass: 'password' 31 | } 32 | }); 33 | 34 | var mailOptions = { 35 | from: 'sender@mail.com', 36 | to: 'receiver@mail.com', 37 | subject: 'Hello world!', 38 | text: 'Plaintext message example.' 39 | }; 40 | 41 | smtpTransport.sendMail(mailOptions, function(err) { 42 | console.log('Message sent!'); 43 | }); 44 | ``` 45 | 46 | Swapping Gmail for another service is as simple as renaming the **service** property 47 | and updating username & password. It's that simple! 48 | 49 | There are many great guides over at [nodemailer.com](http://www.nodemailer.com/), 50 | be sure to check it out! 51 | 52 | As I mentioned earlier, Nodemailer also supports HTML e-mails. Here is 53 | a screenshot from Dan Stroot's [Skeleton](https://github.com/dstroot/skeleton) boilerplate project 54 | which is based on my [Hackathon Starter](github.com/sahat/hackathon-starter) project. 55 | You can take a look at **welcome.jade** template [here](https://github.com/dstroot/skeleton/blob/master/views/mail/welcome.jade). 56 | 57 | ![](images/backend/intermediate/sending-emails-with-nodemailer-1.png) 58 | 59 | Basically the only difference with HTML e-mails is you have to load an HTML template 60 | and assign it to `html` property inside `mailOptions` object. But the problem is we usually 61 | do not send generic e-mails that look the same for all users. In this welcome 62 | message there is one variable that changes depending on who it is sent to: 63 | user's full name. 64 | 65 | Suppose you have that **views/welcome.jade** template, this is how you would 66 | parse it, convert to HTML and send via Nodemailer: 67 | 68 | ``` 69 | res.render('welcome', { 70 | name: 'Satellizer L. Bridget' 71 | }, function(err, html) { 72 | 73 | var mailOptions = { 74 | to: 'satellizer@westgenetics.edu', 75 | from: 'elize.schmitz@westgenetics.edu' 76 | subject: 'Welcome to West Genetics!', 77 | html: html 78 | }; 79 | 80 | smtpTransport.sendMail(mailOptions, function(err) { 81 | console.log('Message sent!'); 82 | }); 83 | }); 84 | ``` 85 | 86 | You probably didn't know that you can provide a callback function to 87 | `res.render`? Neither did I until recently. I have used `res.render` without 88 | any callbacks for so long that I thought that's the only way it works. 89 | But if you look at the Express documentation on 90 | [res.render](http://expressjs.com/3x/api.html#res.render), you will see that 91 | it has the following structure: 92 | 93 | ``` 94 | res.render(view, [locals], callback) 95 | ``` 96 | 97 | This should at least get you started with sending e-mails in Node.js. My advice 98 | is to focus on plaintext e-mails first, since it's easier to work with, and enhance it 99 | with HTML later on as time and resources allow. 100 | 101 |
102 | #### Additional Resources 103 | 104 | 1. [HTML Email Boilerplate](http://htmlemailboilerplate.com/) 105 | 2. [Email Blueprints](https://github.com/mailchimp/Email-Blueprints) 106 | 3. [Antwort](https://github.com/InterNations/antwort) -------------------------------------------------------------------------------- /posts/backend/intermediate/who-is-online-with-socketio.md: -------------------------------------------------------------------------------- 1 |
2 |

When would I use this?

3 | You want to know how many active online visitors are on your site in 4 | real-time. 5 |
6 | 7 | I am sure you have seen plenty of online visitor counters that display how many 8 | users are online on that website. Using websockets we can monitor in real-time 9 | how many connected users are currently online on the website. 10 | 11 | ![](images/backend/intermediate/who-is-online-with-socketio-1.png) 12 | 13 | For this example I will assume you are using Express web framework. Add the 14 | following code somewhere in your app, after `var app = express()`. 15 | 16 | **Server** 17 | ``` 18 | var server = require('http').createServer(app); 19 | var io = require('socket.io').listen(server); 20 | 21 | var userCount = 0; 22 | 23 | io.sockets.on('connection', function (socket) { 24 | userCount++; 25 | io.sockets.emit('userCount', { userCount: userCount }); 26 | socket.on('disconnect', function() { 27 | userCount--; 28 | io.sockets.emit('userCount', { userCount: userCount }); 29 | }); 30 | }); 31 | ``` 32 | 33 | In the `` tag of **index.html** (or **layout.jade**, depending on the 34 | templating engine that you are using) add this line: 35 | 36 | ``` 37 | // HTML 38 | 39 | 40 | // Jade 41 | script(src='/socket.io/socket.io.js') 42 | ``` 43 | 44 | 45 | Then in your client-side JavaScript file add the following code. It 46 | listens for `userCount` socket event and updates the DOM whenever this 47 | event is triggered: user connects to the website, user leaves the website. 48 | 49 | **Client** 50 | ``` 51 | var socket = io.connect(); 52 | 53 | socket.on('userCount', function (data) { 54 | console.log(data.userCount); 55 | }); 56 | ``` 57 | 58 | Here is quick example demonstrating the code. After opening 4 browser windows 59 | on **http://localhost:3000**. 60 | 61 | ![](images/backend/intermediate/who-is-online-with-socketio-2.png) 62 | 63 | ### Source Code 64 |
65 | 66 | **app.js** 67 | ```javascript 68 | var express = require('express'); 69 | var http = require('http'); 70 | var path = require('path'); 71 | 72 | var app = express(); 73 | var server = http.createServer(app); 74 | var io = require('socket.io').listen(server); 75 | 76 | app.set('port', process.env.PORT || 3000); 77 | app.set('views', path.join(__dirname, 'views')); 78 | app.set('view engine', 'jade'); 79 | app.use(express.logger('dev')); 80 | app.use(express.json()); 81 | app.use(express.urlencoded()); 82 | app.use(express.methodOverride()); 83 | app.use(app.router); 84 | app.use(express.static(path.join(__dirname, 'public'))); 85 | 86 | app.get('/', function(req, res) { 87 | res.render('index'); 88 | }); 89 | 90 | server.listen(app.get('port'), function() { 91 | console.log('Express server listening on port ' + app.get('port')); 92 | }); 93 | 94 | 95 | var userCount = 0; 96 | 97 | io.sockets.on('connection', function (socket) { 98 | userCount++; 99 | io.sockets.emit('userCount', { userCount: userCount }); 100 | socket.on('disconnect', function() { 101 | userCount--; 102 | io.sockets.emit('userCount', { userCount: userCount }); 103 | }); 104 | }); 105 | ``` 106 | 107 | **layout.jade** 108 | ```jade 109 | doctype html 110 | html 111 | head 112 | title User Counter 113 | link(rel='stylesheet', href='//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css') 114 | script(src='/socket.io/socket.io.js') 115 | body 116 | block content 117 | script(src='//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js') 118 | script(src='/javascripts/main.js') 119 | ``` 120 | 121 | **index.jade** 122 | ```jade 123 | extends layout 124 | 125 | block content 126 | .container 127 | .page-header 128 | h1 129 | span#userCount 0 130 | small users online 131 | ``` 132 | 133 | **main.js** 134 | ```javascript 135 | var socket = io.connect(); 136 | 137 | socket.on('userCount', function (data) { 138 | console.log(data.userCount); 139 | }); 140 | ``` 141 | 142 |
143 | #### Additional Resources 144 | 145 | 1. [New Eden Faces](http://www.newedenfaces.com) 146 | -------------------------------------------------------------------------------- /posts/frontend/advanced/resizable-split-pane-layout.md: -------------------------------------------------------------------------------- 1 |
2 | Full guide coming in June 2014. 3 |
4 | 5 | #### Demo 6 | Split Pane 7 | 8 | 9 |
10 | #### Additional Resources 11 | 12 | 1. [jQuery Split Plane Plugin](https://github.com/shagstrom/split-pane) 13 | -------------------------------------------------------------------------------- /posts/frontend/advanced/tables-with-remote-data.md: -------------------------------------------------------------------------------- 1 |
2 | Full guide coming in June 2014. 3 |
4 | 5 | #### jQuery 6 | [Data-Tables](https://datatables.net/) 7 | 8 |

Angular.js

9 | [ngTable](http://bazalt-cms.com/ng-table/) 10 | 11 |

Backbone.js

12 | [Backgrid.js](http://backgridjs.com/) 13 | 14 |

Ember.js

15 | [Ember Table](http://addepar.github.io/#/ember-table/overview) 16 | -------------------------------------------------------------------------------- /posts/frontend/beginner/activate-bootstrap-dropdown-on-hover.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sometimes you need to open Bootstrap dropdowns on mouse over, however it is not 4 | always the case, but when you need you **really** need it. If you look around 5 | on the internet, you might stumble upon StackOverflow posts that require 6 | [crazy css hacks](http://stackoverflow.com/questions/8878033/how-to-make-twitter-bootstrap-menu-dropdown-on-hover-rather-than-click) 7 | to get it done. Thankfully there is a nice library called [bootstrap-hover-dropdown](https://github.com/CWSpear/bootstrap-hover-dropdown). 8 | 9 | Download minified JS file from the [GitHub Project](https://github.com/CWSpear/bootstrap-hover-dropdown) 10 | and include it **after** jQuery and Bootstrap.js: 11 | 12 | ```html 13 | 14 | ``` 15 | 16 | Add `data-hover="dropdown"` to the **dropdown-toggle** element. 17 | 18 | ```html 19 |
20 | 23 | 30 |
31 |
32 | 33 | 38 |
39 | ``` 40 | 41 |
42 | 45 | 52 |
53 |
54 | 55 | 60 |
61 |
62 | 63 | You can still use original Bootstrap dropdowns that work on click, just don't include 64 | `data-hover="dropdown"` attribute. 65 | 66 |
67 | #### Additional Resources 68 | 69 | 1. [bootstrap-hover-dropdown](https://github.com/CWSpear/bootstrap-hover-dropdown) 70 | -------------------------------------------------------------------------------- /posts/frontend/beginner/bootstrap-sidebar-menu.md: -------------------------------------------------------------------------------- 1 | You would think creating a sidebar would be easy, considering how 2 | such task is so common. 3 | 4 | If you have used Bootstrap in the past, this would probably be your 5 | first instinct: 6 | 7 | ``` 8 |
9 |
10 | Sidebar 11 |
12 |
13 | Page Content 14 |
15 |
16 | ``` 17 | 18 | **Example** 19 |
20 | Sidebar 21 |
22 |
23 | Page Content 24 |
25 | 26 | I don't know about you, but I usually want my sidebars to have 27 | fixed width, while page content to have fluid width. 28 | 29 | #### Version 1: Auto-hide Sidebar 30 | 31 | Check out this example in [full-screen mode](http://jsfiddle.net/sahat/aAH2g/1/embedded/result/) 32 | and you will notice that sidebar collapses automatically below 767px 33 | browser width. 34 | 35 | **Source:** [Start Bootstrap](http://startbootstrap.com/simple-sidebar) 36 | 37 | 38 | 39 | #### Version 2: Fixed Sidebar + Header 40 | 41 | This example uses affix to automatically highlight current section 42 | in the sidebar as you scroll the page up or down. For best results take 43 | a look at the [full-screen](http://jsfiddle.net/sahat/5PSne/2/embedded/result/) result. 44 | 45 | **Source:** [Bootply](http://www.bootply.com/90936) 46 | 47 | 48 | 49 |
50 | #### Additional Resources 51 | 52 | 1. [Snap.js](http://jakiestfu.github.io/Snap.js/demo/apps/default.html) 53 | 2. [Bootstrap Dashboard Sidebar](http://getbootstrap.com/examples/dashboard/) 54 | 3. [Bootstrap Blog Sidebar](http://getbootstrap.com/examples/blog/) 55 | 56 | -------------------------------------------------------------------------------- /posts/frontend/beginner/color-selector.md: -------------------------------------------------------------------------------- 1 | Download [bootstrap-colorselector](https://github.com/flaute/bootstrap-colorselector) 2 | from GitHub and grab these two files: 3 | 4 | - `bootstrap-colorselector.css` 5 | - `bootstrap-colorselector.js` 6 | 7 | #### 1. Include those files in your HTML like so: 8 | 9 | ```html 10 | 11 | 12 | 13 | 14 | 15 | 16 | ``` 17 | 18 | #### 2. Create a color selector: 19 | 20 | ```html 21 | 29 | ``` 30 | 31 | #### 3. Initialize it: 32 | 33 | ```javascript 34 | $(document).ready(function() { 35 | $('#colorselector').colorselector({ 36 | callback: function (value, color, title) { 37 | $("#colorValue").val(value); 38 | $("#colorColor").val(color); 39 | $("#colorTitle").val(title); 40 | } 41 | }); 42 | }); 43 | ``` 44 | 45 | #### Demo 46 | Color Selector 47 | 48 | -------------------------------------------------------------------------------- /posts/frontend/beginner/comparing-icon-fonts.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |

When would I use this?

7 | If you need to improve usability by adding helpful icons in your web application. 8 | This site uses Font Awesome in various places. 9 |
10 | 11 | If you are building a web application, you would almost certainly use icons at 12 | some point, somewhere in your application. 13 | 14 | If you have used Bootstrap 3+ then you might have seen their [Glyphicons](http://getbootstrap.com/components/). 15 | One advantage over fixed-size image-based icons of Bootstrap 2 is that these 16 | font icons look good at any size, and since it's just a font, you can easily 17 | change its color in CSS. 18 | 19 | #### [Glyphicons](http://getbootstrap.com/components/) 20 | Official Bootstrap's icon font that comes pre-packaged in a zip file when you 21 | download Bootstrap 3. 22 | 23 | **Number of Icons**: 200 24 | 25 | **License**: Free to use 26 | 27 | **Font File Size**: 120 kB 28 | 29 | The only good thing about Glyphicons is they are bundled with Bootstrap. But 30 | compared to other fonts, it is just too limited to be useful, at least in my 31 | case. Now, [Glyphicons Pro](glyphicons.com) (commercial version) on the other 32 | hand is really nice, but you have to pay for it. 33 | 34 |
35 | 36 | #### [Font Awesome](http://fontawesome.github.io) 37 | The iconic font designed for Bootstrap. Includes Sass and LESS support. 38 | 39 | **Number of Icons**: 369 40 | 41 | **License**: SIL OFL 1.1 42 | 43 | **Font File Size**: 356 kB 44 | 45 | Font Awesome is definitely my favorite icon font. It has a good number of icons 46 | that cover most of my use cases. Their web page is nicely designed and they have 47 | awesome documentation on how to integrate Font Awesome via CDN, LESS, Sass. 48 |
49 | 50 | #### [Elusive](http://shoestrap.org/downloads/elusive-icons-webfont/) 51 | Another icon webfont designed for Bootstrap. Includes Sass and LESS support. 52 | 53 | **Number of Icons**: 299 54 | 55 | **License**: SIL OFL 56 | 57 | **Font File Size**: 364 kB 58 | 59 | Another nice icon font. Typically I use it only when I need certain icons that 60 | I cannot get with Font Awesome or because that one particular icon looks nicer 61 | in Elusive Icons set. I really dislike how they make it so hard to download 62 | Elusive Icons in their redesigned UI by placing the actual download link all 63 | the way in the bottom after scrolling past hundreds of icons. 64 | It is so unintuitive that on multiple occasions I had to download Elusive 65 | Icons directly from the GitHub Project. 66 | 67 |
68 | 69 | #### [Foundation](http://zurb.com/playground/foundation-icon-fonts-3) 70 | Iconic web font by ZURB who created Foundation CSS framework. 71 | 72 | **Number of Icons**: 283 73 | 74 | **License**: Free to use 75 | 76 | **Font File Size**: 294 kB 77 | 78 | I have encountered Foundation icon fonts only recently and I must say - I like 79 | it a lot! Some icons look even better than that of Font Awesome and Elusive. 80 | Definitely give it a try if you are not satisfied with other icon fonts. Or 81 | use multiple fonts at once if you don't mind extra bandwidth overhead for your 82 | users. 83 | 84 |
85 | 86 | #### Final Words 87 | I haven't included every icon font here, only the most popular. There are two 88 | more icon fonts that are worth checking out, [Entypo](http://www.entypo.com) 89 | and [IcoMoon](http://icomoon.io), but unfortunately they include fonts 90 | without a stylesheet, so you still have some work to do if you want it to work 91 | similar to icon fonts above. 92 | 93 | Also, take a look at [Fontrello](http://fontello.com). It allows you to select 94 | only the icons you need from multiple icon fonts and generate a new icon font 95 | with just those icons. This is especially significant when you are only using a 96 | few icons from many different icon fonts. Fontrello allows you to select fonts 97 | from: 98 | 99 | - Custom SVG icons from your computer 100 | - Fontelico 101 | - Font Awesome 102 | - Entypo 103 | - Typicons 104 | - Iconic 105 | - Modern Pictograms 106 | - Meteocons 107 | - MFG Labs 108 | - Maki 109 | - Zocial 110 | - Brandico 111 | - Elusive 112 | - Linecons 113 | - Web Symbols 114 | 115 |
116 | #### Additional Resources 117 | 118 | 1. [GitHub Icon Fonts Showcase](https://github.com/showcases/icon-fonts) 119 | 2. [IcoMoon](http://icomoon.io) 120 | 3. [Fontrello - icon fonts generator](http://fontello.com) 121 | -------------------------------------------------------------------------------- /posts/frontend/beginner/image-carousel-with-slick.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sahat/jsrecipes/727cb089ca76a304fcb29e02d0e5f56113e3746d/posts/frontend/beginner/image-carousel-with-slick.md -------------------------------------------------------------------------------- /posts/frontend/beginner/loading-progress-bar.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | You may have noticed the light-blue progress bar on this site when page 23 | is loading. I am using [ngProgress](http://victorbjelkholm.github.io/ngProgress) since 24 | it integrates nicely with Angular.js, but it's basically the same thing as 25 | [NProgress](https://github.com/rstacruz/nprogress/) on which it is based on. 26 | NProgress is also more popular, actively maintained and is not Angular-specific, 27 | so that's what I will cover here. 28 | 29 |
30 | Using this loading progress bar really only makes 31 | sense if you have an AJAX-based application, i.e. built on Angular, Ember, 32 | Backbone, React, etc. 33 |
34 | Download the [latest version](https://github.com/rstacruz/nprogress/releases) 35 | of NProgress, then include both CSS and JS files in your HTML. 36 | 37 | ``` 38 | 39 | NProgress.start() — shows the progress bar. 46 | 47 | — sets a percentage. 48 | 49 | — increments by a little. 50 | 51 | — completes the progress. 52 | 53 | You will mostly use `start()` right before you make an AJAX request 54 | and `done()` when AJAX request successfully completes. For advanced usage 55 | and configuration of NProgress refer to the official 56 | [README](https://github.com/rstacruz/nprogress/#advanced-usage). 57 | 58 | I don't know much about Ember, but in Backbone I use jQuery 59 | `ajaxStart` and `ajaxComplete` functions to start and finish 60 | progress bars. 61 | 62 | ``` 63 | $(document).ajaxStart(function() { 64 | NProgress.start(); 65 | }); 66 | 67 | $(document).ajaxComplete(function() { 68 | NProgress.done(); 69 | }); 70 | ``` 71 | 72 |
73 | #### Additional Resources 74 | 75 | 1. [ngProgress for Angular.js](http://victorbjelkholm.github.io/ngProgress) 76 | 2. [pace - automatic page load progress bar](http://github.hubspot.com/pace/) 77 | 78 | -------------------------------------------------------------------------------- /posts/frontend/beginner/pinterest-grid-layout.md: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Creating a dynamic grid of thumbnails or `
` containers is super 7 | easy with [Masonry](http://masonry.desandro.com). If you've run across 8 | this UI pattern, they are probably using Masonry, or its variations: 9 | [Isotope](http://isotope.metafizzy.co), [Packery](http://packery.metafizzy.co/), 10 | [Wookmark](http://www.wookmark.com/jquery-plugin). They are all 11 | pretty similar, but each with a distinguishing feature. For example, I use 12 | [Isotope](http://isotope.metafizzy.co) instead of Masonry on 13 | [Allison Eckfeldt's](http://kazlovesbats.com) website, because I need to sort 14 | thumbnails by date. 15 | 16 | ![](images/frontend/beginner/pinterest-grid-layout-1.png) 17 | 18 | Masonry is just a grid layout, it doesn't have anything like sorting or 19 | filtering. For that you should use Isotope instead. 20 | 21 | #### Getting Started 22 | Download [masonry.pkgd.min.js](http://masonry.desandro.com/masonry.pkgd.min.js) 23 | and include it in your HTML: 24 | 25 | ```html 26 | 27 | ``` 28 | 29 | Add this to your stylesheet: 30 | ```css 31 | .item { 32 | width: 200px; 33 | } 34 | ``` 35 | 36 | Masonry works on a container element with a group of similar child items. Now, 37 | to initialize Masonry, you can do it with JavaScript or in HTML, without writing 38 | any JavaScript. I prefer the HTML approach because it is simpler. Just add 39 | `js-masonry` to the class of the container element and set options via 40 | `data-masonry-options`. Here is a [full list of options](http://masonry.desandro.com/options.html) 41 | with code and visual examples. 42 | 43 | ```html 44 |
45 |
Each Masonry element is 200px wide
46 |
For Masonry to work, each item must have a common class name such as .item
47 |
As you resize the browser window, grid layout will adjust automatically
48 |
You can also specify the gutter width between each element via gutter option
49 |
Masonry can be initialized using JavaScript or in HTML
50 |
51 | ``` 52 | If you prefer to initialize it with JavaScript, here is how you would do it: 53 | ```javascript 54 | // Pure JS 55 | var container = document.querySelector('#container'); 56 | var msnry = new Masonry(container, { 57 | gutter: 10, 58 | columnWidth: 200, 59 | itemSelector: '.item' 60 | }); 61 | 62 | /** OR **/ 63 | 64 | // jQuery 65 | $('#container').masonry({ 66 | gutter: 10, 67 | columnWidth: 200, 68 | itemSelector: '.item' 69 | }) 70 | ``` 71 | 72 | #### Demo 73 | 74 | #### Final Notes 75 | If you are planning on doing something more complex with this grid layout, 76 | take a look at [Masonry Methods](http://masonry.desandro.com/methods.html). 77 | For instance, you could dynamically append new items to the grid, perhaps in 78 | combination with [infinite scrolling](#!/frontend/infinite-scrolling)? 79 | 80 | For a unique bin-packing algorithm (fill all empty gaps) and draggable 81 | interactions use [Packery](http://packery.metafizzy.co/). For filtering, 82 | sorting and multiple layout algorithms use [Isotope](isotope.metafizzy.co). 83 | -------------------------------------------------------------------------------- /posts/frontend/intermediate/handling-keyboard-shortcuts-in-javascript.md: -------------------------------------------------------------------------------- 1 | 26 | 27 |
28 |

When would I use this?

29 | Keyboard shortcuts in a web application can sometimes dramatically improve 30 | user experience. A perfect use case for keyboard shortcuts interaction 31 | would be single page applications like Gmail, Cloud9 IDE , JSFiddle. 32 |
33 | 34 | 35 | 36 | #### Quick Demo 37 | Press any number (1-4) on your keyboard. 38 | 39 | 47 | 48 |
49 | 50 |
51 | Full guide coming in May 2014. Meanwhile, refer to Mousetrap 52 | documentation. 53 |
-------------------------------------------------------------------------------- /posts/frontend/intermediate/infinite-scrolling.md: -------------------------------------------------------------------------------- 1 | #### jQuery 2 | [Infinite Scroll](http://www.infinite-scroll.com) 3 | 4 |

Angular.js

5 | [ngInfiniteScroll](http://binarymuse.github.io/ngInfiniteScroll) 6 | 7 |

Backbone.js

8 | [backbone-pageable](https://github.com/backbone-paginator/backbone-pageable) 9 | 10 | [Lightweight Infinite Scrolling using Twitter API](backbonetutorials.com/infinite-scrolling) 11 | 12 |

Ember.js

13 | [Ember Infinite Scroll](https://github.com/bantic/ember-infinite-scroll) 14 | 15 |
16 | Full guide coming in May 2014. 17 |
18 | 19 |
20 | #### Additional Resources 21 | 22 | 1. [10 Reasons Not To Use Infinite Scroll On Your Website](http://geeks.bizzabo.com/10-reasons-not-to-use-infinite-scroll-on-your-website) 23 | -------------------------------------------------------------------------------- /posts/frontend/intermediate/instant-page-load-with-instantclick.md: -------------------------------------------------------------------------------- 1 |
2 |

When would I use this?

3 | If you want to improve page load performance, InstantClick essentially 4 | gives you a free speed boost by prefetching a page on link mouseover. 5 |
6 | 7 |

InstantClick

8 | 9 | [InstantClick](http://instantclick.io/) is a JavaScript library that 10 | dramatically speeds up your website, making navigation effectively instant in 11 | most cases. 12 | 13 | #### How it works 14 | Before visitors click on a link, they hover over that link. Between these two 15 | events, 200 ms to 300 ms usually pass by (test yourself here). InstantClick 16 | makes use of that time to preload the page, so that the page is already there 17 | when you click. 18 | 19 |
20 | Full guide coming in May 2014. 21 |
-------------------------------------------------------------------------------- /posts/frontend/intermediate/offline-status-notification.md: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | To test this demo, turn off your WiFi and you will see a different status 24 | message. 25 | 26 | 29 | 30 | 33 | 34 | Here is a source code behind this very simple demo: 35 | ```script 36 | // Options 37 | Offline.options = { 38 | checkOnLoad: true 39 | }; 40 | // Events 41 | Offline.on('confirmed-up', function() { 42 | $('.alert-danger').fadeOut(); 43 | $('.alert-success').fadeIn(); 44 | }); 45 | Offline.on('confirmed-down', function() { 46 | $('.alert-success').fadeOut(); 47 | $('.alert-danger').fadeIn(); 48 | }); 49 | } 50 | ``` 51 | 52 |
53 | Full guide coming in May 2014. For now please refer to 54 | Offline.js Documentation. 55 |
-------------------------------------------------------------------------------- /posts/frontend/intermediate/parallax-effect.md: -------------------------------------------------------------------------------- 1 |
2 | Full guide coming in June 2014. 3 |
4 | 5 |
6 | #### Additional Resources 7 | 8 | 1. [How to create a parallax scrolling website](http://ihatetomatoes.net/how-to-create-a-parallax-scrolling-website/) 9 | 2. [How to integrate simple Parallax with Bootstrap](http://untame.net/2013/04/how-to-integrate-simple-parallax-with-twitter-bootstrap) 10 | 3. [Bootstrap Scroller parallax template one page responsive wireframe](https://coderwall.com/p/mk4kea) 11 | 4. [Parallax.js](http://stolksdorf.github.io/Parallaxjs/) 12 | 5. [Parallax](https://github.com/wagerfield/parallax) 13 | -------------------------------------------------------------------------------- /posts/frontend/intermediate/search-filter-sort-list-or-tables.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | Full guide coming in May 2014. 5 |
6 | 7 | Demo of a **sortable** and **filterable** table using [List.js](http://listjs.com/). 8 | It works with tables that have pre-existing data defined in HTML. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /posts/frontend/intermediate/star-rating-plugin.md: -------------------------------------------------------------------------------- 1 |
2 | Full guide coming in May 2014. 3 |
4 | 5 | Meanwhile, check out the source code for the project below: [client-side usage](https://github.com/sahat/csc322/blob/master/public/js/custom.js#L196) 6 | and [server-side handling](https://github.com/sahat/csc322/blob/master/server.js#L474). 7 | And here is a fully working [demo](http://csc322.herokuapp.com). 8 | 9 | **Note:** That was my very first Node.js project with very little client-side 10 | JavaScript experience from over 2 years ago. I do not recommend using it as a 11 | learning resource. 12 | 13 | ![](images/frontend/intermediate/star-rating-plugin-1.png) 14 | 15 |
16 | #### Additional Resources 17 | 18 | 1. [jQuery Raty](http://wbotelhos.com/raty/) 19 | -------------------------------------------------------------------------------- /posts/general/coding-like-a-pro-with-emmet.md: -------------------------------------------------------------------------------- 1 | Emmet is a plugin for many popular text editors like Vim, Sublime Text, 2 | Eclipse, WebStorm, Espresso, which greatly improves HTML and CSS workflow. 3 | It allows you to quickly expand simple abbreviations into snippets of code with 4 | a Tab key. 5 | 6 |
7 | Press Tab to expand abbreviations. 8 |
9 | 10 | #### Create new HTML document 11 | Document 12 | 13 | #### Add .class or #id to an element 14 | JS Bin 15 | 16 | #### Child Nesting 17 | JS Bin 18 | 19 | #### Multiplication 20 | JS Bin 21 | 22 | #### Numbering 23 | JS Bin 24 | 25 |
26 | 27 | For more HTML and CSS snippets refer to the Emmet Cheat Sheet. 28 | 29 | #### Additional Resources 30 | 31 | 1. [Emmet Cheat Sheet](http://docs.emmet.io/cheat-sheet/) 32 | 2. [Faster Workflow: Mastering Emmet](http://www.sitepoint.com/faster-workflow-mastering-emmet-part-1/) 33 | 3. [Using Emmet to Speed Up Front-End Web Development](http://blog.teamtreehouse.com/using-emmet-speed-front-end-web-development) 34 | 35 | -------------------------------------------------------------------------------- /posts/general/development-workflow.md: -------------------------------------------------------------------------------- 1 | #### JavaScript Development Workflow of 2013 by Paul Irish 2 | 3 |
4 | 5 |
-------------------------------------------------------------------------------- /posts/general/javascript-style-guide.md: -------------------------------------------------------------------------------- 1 | It is very important to have a consistent programming style in your code. For one 2 | it shows that you care about your code. Secondly, it is easier to read your code 3 | when everything is consistent. Unlike Python language that has the 4 | [PEP8 Style Guide](http://legacy.python.org/dev/peps/pep-0008/), JavaScript 5 | is divided between the following major style guides: 6 | 7 | 1. [Google](https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) 8 | 2. [Airbnb ](https://github.com/airbnb/javascript) 9 | 3. [NPM](https://www.npmjs.org/doc/misc/npm-coding-style.html) 10 | 4. [jQuery](https://contribute.jquery.org/style-guide/js/) 11 | 5. [Douglas Crockford](http://javascript.crockford.com/code.html) 12 | 6. [Idiomatic.js](https://github.com/rwaldron/idiomatic.js) 13 | 14 |
15 | Everyone is going to have a different opinion for what is the right style guide. 16 | All that really matters is that you pick one, and stick with it. I will give 17 | my opinion on each style guide below, but take it with a grain of salt, it is only 18 | my opinion, you do not have to agree with me on it. 19 |
20 | 21 | #### Google Style Guide 22 | Overall I like this it. It was the first JavaScript style guide that I used. I 23 | don't remember why I stopped using it, maybe the information wasn't laid out as 24 | nicely as Airbnb or Idiomatic.js, but instead hidden behind those annoying 25 | dropdowns. As far as style goes, I tend to agree with most of the points. 26 | 27 | #### Airbnb Style Guide 28 | This is my preferred style guide. Information is laid out nicely with DO's and 29 | DON'Ts. There is one thing I disagree with them on: **variables**. 30 | 31 | ``` 32 | // bad 33 | var items = getItems(); 34 | var goSportsTeam = true; 35 | var dragonball = 'z'; 36 | 37 | // good 38 | var items = getItems(), 39 | goSportsTeam = true, 40 | dragonball = 'z'; 41 | ``` 42 | 43 | I prefer their "bad" approach because I don't have to deal with the mess you create 44 | when swapping or deleting variables. If you want to delete `dragonball` variable, 45 | you have to cut/delete that line, then go up one line to `goSportsTeam` and change comma to semicolon. 46 | Or what if you want to swap `dragonball` with `goSportsTeam`? In my editor 47 | I can do it with `Command + Shift + Up Arrow`, but now I have to change semicolon 48 | on `dragonball` to comma, and do the reverse for `goSportsTeam`. If you are still 49 | not convinced, guess how many steps must you take if you want to remove `items` 50 | variable? 51 | 52 | Other than that I really like Airbnb style guide. 53 | 54 | #### NPM Style Guide 55 | My least favorite style guide due to their highly annoying "comma-first" usage 56 | and not using semicolons. Whenever I see this code style, I immediately convert it. 57 | 58 | ``` 59 | var a = 1 60 | , b = "abc" 61 | , etc 62 | , somethingElse 63 | ``` 64 | 65 | #### jQuery Style Guide 66 | Despite being one of the most popular style guides, I disagree with them on 67 | spacing, variables (same as Airbnb) and double quotes. 68 | 69 | #### Crockford's Style Guide 70 | Another solid style guide, although I would vote in favor of Google and Airbnb 71 | instead. It's probably barely worth mentioning, but I don't like Crockford's use 72 | of a space character between `function` and `()`, e.g. 73 | 74 | ``` 75 | div.onclick = function (e) { 76 | return false; 77 | }; 78 | ``` 79 | 80 | #### Idiomatic.js Style Guide 81 | I have not used this style guide, so there's nothing for me to comment on. In 82 | either case I thought I should list it here considering it's one of the most 83 | popular style guides out there. 84 | 85 | #### Conclusion 86 | Once again, there is no right or wrong style guide here. Whichever style you pick is 87 | the right one, as long as you are consistent with it. 88 | 89 | On a personal note, 90 | I really wish college professors would encourage following a coding style guide 91 | early in the Computer Science and Computer Engineering curriculum, 92 | especially those starting out with Java and C++. -------------------------------------------------------------------------------- /posts/general/webstorm-ide-tips.md: -------------------------------------------------------------------------------- 1 | #### What is WebStorm? 2 | 3 | [WebStorm](http://www.jetbrains.com/webstorm/) is a modern JavaScript IDE 4 | developed by JetBrains. Unlike Vim, you don't have to spend months learning the tool to be 5 | productive. Simply opening your project in WebStorm instantly makes you far 6 | more productive than any other text editor thanks to auto-completion, 7 | smart inspections that can detect dead code, syntax errors, unreachable code, 8 | references to local files that don't exist (likely due to misspelling). 9 | 10 | Let me get something out of the way first. If you use Vim that's okay, 11 | stick with it. If you use Sublime Text, that's alright too. Use 12 | whichever tool that makes you more productive. That's all that matters at the 13 | end of the day. 14 | 15 | #### 1. Create a ` 29 | ``` 30 | 31 | That's not what I wanted! So I end up manually typing `src="insert url here"` for 32 | each script. That gets mildly annoying after doing it so many times. 33 | 34 | It turns out there is a snippet named `script:src`. It expands into exactly 35 | what you might expect: 36 | 37 | ```html 38 | 39 | ``` 40 | 41 | #### 2. Create a new file that doesn't exist yet 42 | Let's suppose for a moment you are working on a new project. You don't have any 43 | styles yet, so inside **index.html** you add: 44 | 45 | ``` 46 | 47 | ``` 48 | 49 | WebStorm will immediately tell you that **style.css** does not exist. If your place 50 | a cursor over *css* in `css/style.css` and press Alt + Enter, it will prompt 51 | you to create `css` directory. After doing that the yellow highlight will go away from 52 | `css`, meaning there are no problems. Now move your cursor to *style.css*, press 53 | Alt + Enter and it will prompt you to create `style.css` file inside `css` folder. 54 | I think that's pretty neat! 55 | 56 | ![](images/general/webstorm-ide-tips-1.png) 57 | 58 | #### 3. Reformat Code 59 | 60 | When you are working on code in a team, unfortunately not everyone follows the 61 | same code style. This is especially true for open-source GitHub projects. There 62 | is an easy way to fix. 63 | 64 | 65 | To reformat code press Alt + Command + L, it will format your code according to 66 | the code style defined in **Preferences > Code Style**. 67 | 68 | ![](images/general/webstorm-ide-tips-2.png) 69 | 70 | #### 4. Useful Keyboard Shortcuts 71 | 72 | 73 | 74 | 75 | 76 | 77 | 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 |
ShortcutDescription
Control + TabSwitch between the tool windows and files opened in the editor.
Alt + EnterShow the list of available intention actions.
Alt + Command + JSurround with a live template.
Command + /Comment or uncomment a line or fragment of code.
Command + DDuplicate the current line or selection.
Alt + Up/DownIncremental selection.
106 | 107 | #### 5. CSS Auto-completion and Auto-suggestions 108 | I was trying to convert an image icon to Base64 using this [Base64 Converter](http://webcodertools.com/imagetobase64converter). 109 | I coped the `background-image` source code and pasted into my stylesheet. If 110 | you just paste that code, image will be repeated in all directions. Obviously 111 | that's not what I wanted. I then start typing `background-repeat:`, but I forgot 112 | the exact syntax to prevent background image from repeating. Thankfully, 113 | WebStorm can display auto-completion popup with Control + Space. 114 | 115 | ![](images/general/webstorm-ide-tips-3.png) 116 | 117 | After I added `background-repeat` and `background-position` properties, WebStorm 118 | highlighted all those properties with a squiggly line, suggesting me to merge 119 | them into a single `background` property. 120 | 121 | ![](images/general/webstorm-ide-tips-4.png) 122 | 123 | 124 | 125 |
126 | #### Additional Resources 127 | 128 | 1. [WebStorm Tricks and Tips by John Lindquist (55min screencast)](https://www.youtube.com/watch?v=leKbqNpgoNQ) 129 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /scripts/app.js: -------------------------------------------------------------------------------- 1 | angular.module('MyApp', ['ngRoute', 'ngProgress', 'ngDisqus', 'ngTable']) 2 | .config(['$routeProvider', '$locationProvider', '$disqusProvider', function($routeProvider, $locationProvider, $disqusProvider) { 3 | $disqusProvider.setShortname('jsrecipes'); 4 | $locationProvider.hashPrefix('!'); 5 | 6 | $routeProvider 7 | .when('/', { 8 | templateUrl: 'views/main.html', 9 | controller: 'MainCtrl' 10 | }) 11 | .when('/backend/:name', { 12 | templateUrl: 'views/detail.html', 13 | controller: 'MainCtrl' 14 | }) 15 | .when('/frontend/:name', { 16 | templateUrl: 'views/detail.html', 17 | controller: 'MainCtrl' 18 | }) 19 | .when('/general/:name', { 20 | templateUrl: 'views/detail.html', 21 | controller: 'MainCtrl' 22 | }) 23 | .when('/feedback', { 24 | templateUrl: 'views/feedback.html', 25 | controller: 'MainCtrl', 26 | title: 'Feedback' 27 | }) 28 | .when('/contribute', { 29 | templateUrl: 'views/contribute.html', 30 | controller: 'MainCtrl', 31 | title: 'Contribute' 32 | }) 33 | .otherwise({ 34 | templateUrl: 'views/404.html', 35 | controller: 'MainCtrl', 36 | title: 'Not Found' 37 | }); 38 | }]); 39 | -------------------------------------------------------------------------------- /scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | angular.module('MyApp') 2 | .controller('MainCtrl', ['$scope', '$rootScope', '$route', '$window', 'Post', 'GitHub', '$routeParams', 'ngProgress' ,function($scope, $rootScope, $route, $window, Post, GitHub, $routeParams, ngProgress) { 3 | $scope.$on('$routeChangeSuccess', function($currentRoute, $previousRoute) { 4 | if ($routeParams.name) { 5 | ngProgress.start(); 6 | Post.getBySlug($routeParams.name, function(post) { 7 | $scope.post = post; 8 | ngProgress.complete(); 9 | $window.document.title = $scope.post.title + " - " + 'JS Recipes' 10 | 11 | // Get last commit date 12 | GitHub.lastCommit(post.file, function(data, status, headers, config) { 13 | if (status === 0) return $scope.lastUpdated = 'Unknown'; 14 | $scope.lastUpdated = new Date(data[0].commit.committer.date).toLocaleString(); 15 | }); 16 | }); 17 | } else { 18 | $window.document.title = $route.current.title ? $route.current.title + " - " + 'JS Recipes' : 'JS Recipes'; 19 | 20 | $rootScope.title = $route.current.title; 21 | 22 | Post.getPosts(function(posts) { 23 | $scope.posts = posts; 24 | }); 25 | 26 | GitHub.getContributors(function(contributors) { 27 | $scope.contributors = contributors.slice(0,10); 28 | }); 29 | } 30 | }); 31 | }]); 32 | -------------------------------------------------------------------------------- /scripts/directives/markdown.js: -------------------------------------------------------------------------------- 1 | angular.module('MyApp') 2 | .directive('markdown', ['$http', '$compile', function($http, $compile) { 3 | var converter = new Showdown.converter(); 4 | return { 5 | link: function(scope, element, attrs) { 6 | attrs.$observe('file', function(file) { 7 | if (file) { 8 | $http.get('posts/' + file).success(function(response) { 9 | var htmlText = converter.makeHtml(response); 10 | element.html(htmlText); 11 | $compile(element.contents())(scope); 12 | $('pre code').each(function(i, e) { 13 | hljs.highlightBlock(e); 14 | }); 15 | }); 16 | } 17 | }); 18 | } 19 | } 20 | }]); -------------------------------------------------------------------------------- /scripts/directives/scroll.js: -------------------------------------------------------------------------------- 1 | angular.module('MyApp') 2 | .directive('scroll', ['$window', function($window) { 3 | return function(scope, element, attrs) { 4 | angular.element($window).bind('scroll', function() { 5 | if (this.pageYOffset >= 150) { 6 | $('.edit-on-github').fadeIn(200); 7 | } else { 8 | $('.edit-on-github').fadeOut(200); 9 | } 10 | }); 11 | } 12 | }]); -------------------------------------------------------------------------------- /scripts/lib/angular-disqus.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * angular-disqus 1.1.0 3 | * http://github.com/kirstein/angular-disqus 4 | * 5 | * Licensed under the MIT license 6 | */ 7 | !function(a,b){"use strict";var c=a.module("ngDisqus",[]);c.provider("$disqus",function(){function c(){return document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]}function d(){return l||b.disqus_shortname}function e(a,b){return"//"+a+".disqus.com/"+b}function f(a){var b=document.createElement("script");return b.type="text/javascript",b.async=!0,b.src=a,b}function g(a,b){var c,d,e=a.getElementsByTagName("script");for(d=0;d
',link:function(c){c.$watch("id",function(c){a.isDefined(c)&&b.commit(c)})}}}]),c.directive("disqusIdentifier",["$disqus",function(a){return{restrict:"A",link:function(b,c,d){a.loadCount(d.disqusIdentifier)}}}])}(angular,this); -------------------------------------------------------------------------------- /scripts/lib/angular-route.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.2.17-build.89+sha.34d0740 3 | (c) 2010-2014 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(n,e,A){'use strict';function x(s,g,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,w){function y(){p&&(p.remove(),p=null);h&&(h.$destroy(),h=null);l&&(k.leave(l,function(){p=null}),p=l,l=null)}function v(){var b=s.current&&s.current.locals;if(e.isDefined(b&&b.$template)){var b=a.$new(),d=s.current;l=w(b,function(d){k.enter(d,null,l||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});h=d.scope=b;h.$emit("$viewContentLoaded");h.$eval(u)}else y()} 7 | var h,l,p,t=b.autoscroll,u=b.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var w=e(c.contents());b.controller&&(f.$scope=a,f=g(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,c){return e.extend(new (e.extend(function(){}, 8 | {prototype:a})),c)}function g(a,e){var b=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},k=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;k.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&g(a,c));if(a){var b= 9 | "/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},g(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,g,n,v,h){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart", 10 | d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?c.path(t(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?g.get(d):g.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=h.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl= 11 | b,c=n.get(b,{cache:v}).then(function(a){return a.data})));e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function p(){var a,b;e.forEach(k,function(f,k){var q;if(q=!b){var g=c.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var h=1,p=g.length;h")(i);j.append(k),i.count=f,void 0!==g&&k.eq(0).children().css("height",g),void 0!==h&&(k.eq(0).children().css("background-color",h),k.eq(0).children().css("color",h));var l=0;return{start:function(){this.show();var a=this;l=setInterval(function(){if(isNaN(f))clearInterval(l),f=0,a.hide();else{var b=100-f;f+=.15*Math.pow(1-Math.sqrt(b),2),a.updateCount(f)}},200)},updateCount:function(a){i.count=a,i.$$phase||i.$apply()},height:function(a){return void 0!==a&&(g=a,i.height=g,i.$$phase||i.$apply()),g},color:function(a){return void 0!==a&&(h=a,i.color=h,i.$$phase||i.$apply()),h},hide:function(){k.children().css("opacity","0");var a=this;e(function(){k.children().css("width","0%"),e(function(){a.show()},500)},500)},show:function(){e(function(){k.children().css("opacity","1")},100)},status:function(){return f},stop:function(){clearInterval(l)},set:function(a){return this.show(),this.updateCount(a),f=a,clearInterval(l),f},css:function(a){return k.children().css(a)},reset:function(){return clearInterval(l),f=0,this.updateCount(f),0},complete:function(){f=100,this.updateCount(f);var a=this;return e(function(){a.hide(),e(function(){f=0,a.updateCount(f)},500)},1e3),f}}}],this.setColor=function(a){return void 0!==a&&(this.color=a),this.color},this.setHeight=function(a){return void 0!==a&&(this.height=a),this.height}}),angular.module("ngProgress.directive",[]).directive("ngProgress",["$window","$rootScope",function(a,b){var c={replace:!0,restrict:"E",link:function(a,c){b.$watch("count",function(b){(void 0!==b||null!==b)&&(a.counter=b,c.eq(0).children().css("width",b+"%"))}),b.$watch("color",function(b){(void 0!==b||null!==b)&&(a.color=b,c.eq(0).children().css("background-color",b),c.eq(0).children().css("color",b))}),b.$watch("height",function(b){(void 0!==b||null!==b)&&(a.height=b,c.eq(0).children().css("height",b))})},template:'
'};return c}]),angular.module("ngProgress",["ngProgress.directive","ngProgress.provider"]); -------------------------------------------------------------------------------- /scripts/services/github.js: -------------------------------------------------------------------------------- 1 | angular.module('MyApp') 2 | .factory('GitHub', ['$http', function($http) { 3 | return { 4 | lastCommit: function(file, callback) { 5 | $http.get('https://api.github.com/repositories/17648824/commits?path=posts/' + file) 6 | .success(callback) 7 | .error(callback) 8 | }, 9 | getContributors: function(callback) { 10 | $http.get('https://api.github.com/repos/sahat/jsrecipes/contributors') 11 | .success(callback) 12 | .error(callback) 13 | } 14 | } 15 | }]); 16 | -------------------------------------------------------------------------------- /scripts/services/post.js: -------------------------------------------------------------------------------- 1 | angular.module('MyApp') 2 | .factory('Post', ['Posts', function(Posts) { 3 | return { 4 | getPosts: function(callback) { 5 | callback(Posts); 6 | }, 7 | getBySlug: function(slug, callback) { 8 | this.getPosts(function(data) { 9 | var recursiveGetProperty = function(obj, lookup, callback) { 10 | for (var property in obj) { 11 | if (property == lookup) { 12 | callback(obj); 13 | } else if (obj[property] instanceof Object) { 14 | recursiveGetProperty(obj[property], lookup, callback); 15 | } 16 | } 17 | }; 18 | recursiveGetProperty(data, 'slug', function(obj) { 19 | if (obj.slug === slug) { 20 | callback(obj); 21 | } 22 | }); 23 | }) 24 | } 25 | } 26 | }]); 27 | -------------------------------------------------------------------------------- /styles/lib/_xcode.scss: -------------------------------------------------------------------------------- 1 | .hljs{display:block;padding:.5em;background:#fff;color:black}.hljs-comment,.hljs-template_comment,.hljs-javadoc,.hljs-comment *{color:#006a00}.hljs-keyword,.hljs-literal,.nginx .hljs-title{color:#aa0d91}.method,.hljs-list .hljs-title,.hljs-tag .hljs-title,.setting .hljs-value,.hljs-winutils,.tex .hljs-command,.http .hljs-title,.hljs-request,.hljs-status{color:#008}.hljs-envvar,.tex .hljs-special{color:#660}.hljs-string{color:#c41a16}.hljs-tag .hljs-value,.hljs-cdata,.hljs-filter .hljs-argument,.hljs-attr_selector,.apache .hljs-cbracket,.hljs-date,.hljs-regexp{color:#080}.hljs-sub .hljs-identifier,.hljs-pi,.hljs-tag,.hljs-tag .hljs-keyword,.hljs-decorator,.ini .hljs-title,.hljs-shebang,.hljs-prompt,.hljs-hexcolor,.hljs-rules .hljs-value,.css .hljs-value .hljs-number,.hljs-symbol,.hljs-symbol .hljs-string,.hljs-number,.css .hljs-function,.clojure .hljs-title,.clojure .hljs-built_in,.hljs-function .hljs-title,.coffeescript .hljs-attribute{color:#1c00cf}.hljs-class .hljs-title,.haskell .hljs-type,.smalltalk .hljs-class,.hljs-javadoctag,.hljs-yardoctag,.hljs-phpdoc,.hljs-typename,.hljs-tag .hljs-attribute,.hljs-doctype,.hljs-class .hljs-id,.hljs-built_in,.setting,.hljs-params,.clojure .hljs-attribute{color:#5c2699}.hljs-variable{color:#3f6e74}.css .hljs-tag,.hljs-rules .hljs-property,.hljs-pseudo,.hljs-subst{color:#000}.css .hljs-class,.css .hljs-id{color:#9b703f}.hljs-value .hljs-important{color:#f70;font-weight:bold}.hljs-rules .hljs-keyword{color:#c5af75}.hljs-annotation,.apache .hljs-sqbracket,.nginx .hljs-built_in{color:#9b859d}.hljs-preprocessor,.hljs-preprocessor *,.hljs-pragma{color:#643820}.tex .hljs-formula{background-color:#EEE;font-style:italic}.diff .hljs-header,.hljs-chunk{color:#808080;font-weight:bold}.diff .hljs-change{background-color:#bccff9}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-comment .hljs-yardoctag{font-weight:bold}.method .hljs-id{color:#000} -------------------------------------------------------------------------------- /styles/lib/bootstrap/_alerts.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: $alert-padding; 11 | margin-bottom: $line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: $alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing $headings-color 19 | color: inherit; 20 | } 21 | // Provide class for links that match alerts 22 | .alert-link { 23 | font-weight: $alert-link-font-weight; 24 | } 25 | 26 | // Improve alignment and spacing of inner content 27 | > p, 28 | > ul { 29 | margin-bottom: 0; 30 | } 31 | > p + p { 32 | margin-top: 5px; 33 | } 34 | } 35 | 36 | // Dismissable alerts 37 | // 38 | // Expand the right padding and account for the close button's positioning. 39 | 40 | .alert-dismissable { 41 | padding-right: ($alert-padding + 20); 42 | 43 | // Adjust close link position 44 | .close { 45 | position: relative; 46 | top: -2px; 47 | right: -21px; 48 | color: inherit; 49 | } 50 | } 51 | 52 | // Alternate styles 53 | // 54 | // Generate contextual modifier classes for colorizing the alert. 55 | 56 | .alert-success { 57 | @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); 58 | } 59 | .alert-info { 60 | @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); 61 | } 62 | .alert-warning { 63 | @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text); 64 | } 65 | .alert-danger { 66 | @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text); 67 | } 68 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_badges.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: $font-size-small; 12 | font-weight: $badge-font-weight; 13 | color: $badge-color; 14 | line-height: $badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: $badge-bg; 19 | border-radius: $badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | 26 | // Quick fix for badges in buttons 27 | .btn & { 28 | position: relative; 29 | top: -1px; 30 | } 31 | .btn-xs & { 32 | top: 0; 33 | padding: 1px 5px; 34 | } 35 | } 36 | 37 | // Hover state, but only for links 38 | a.badge { 39 | &:hover, 40 | &:focus { 41 | color: $badge-link-hover-color; 42 | text-decoration: none; 43 | cursor: pointer; 44 | } 45 | } 46 | 47 | // Account for counters in navs 48 | a.list-group-item.active > .badge, 49 | .nav-pills > .active > a > .badge { 50 | color: $badge-active-color; 51 | background-color: $badge-active-bg; 52 | } 53 | .nav-pills > li > a > .badge { 54 | margin-left: 3px; 55 | } 56 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal; 8 | margin-bottom: $line-height-computed; 9 | list-style: none; 10 | background-color: $breadcrumb-bg; 11 | border-radius: $border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | content: "#{$breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 18 | padding: 0 5px; 19 | color: $breadcrumb-color; 20 | } 21 | } 22 | 23 | > .active { 24 | color: $breadcrumb-active-color; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_button-groups.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Button groups 3 | // -------------------------------------------------- 4 | 5 | // Make the div behave like a button 6 | .btn-group, 7 | .btn-group-vertical { 8 | position: relative; 9 | display: inline-block; 10 | vertical-align: middle; // match .btn alignment given font-size hack above 11 | > .btn { 12 | position: relative; 13 | float: left; 14 | // Bring the "active" button to the front 15 | &:hover, 16 | &:focus, 17 | &:active, 18 | &.active { 19 | z-index: 2; 20 | } 21 | &:focus { 22 | // Remove focus outline when dropdown JS adds it after closing the menu 23 | outline: none; 24 | } 25 | } 26 | } 27 | 28 | // Prevent double borders when buttons are next to each other 29 | .btn-group { 30 | .btn + .btn, 31 | .btn + .btn-group, 32 | .btn-group + .btn, 33 | .btn-group + .btn-group { 34 | margin-left: -1px; 35 | } 36 | } 37 | 38 | // Optional: Group multiple button groups together for a toolbar 39 | .btn-toolbar { 40 | margin-left: -5px; // Offset the first child's margin 41 | @include clearfix(); 42 | 43 | .btn-group, 44 | .input-group { 45 | float: left; 46 | } 47 | > .btn, 48 | > .btn-group, 49 | > .input-group { 50 | margin-left: 5px; 51 | } 52 | } 53 | 54 | .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { 55 | border-radius: 0; 56 | } 57 | 58 | // Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match 59 | .btn-group > .btn:first-child { 60 | margin-left: 0; 61 | &:not(:last-child):not(.dropdown-toggle) { 62 | @include border-right-radius(0); 63 | } 64 | } 65 | // Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it 66 | .btn-group > .btn:last-child:not(:first-child), 67 | .btn-group > .dropdown-toggle:not(:first-child) { 68 | @include border-left-radius(0); 69 | } 70 | 71 | // Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) 72 | .btn-group > .btn-group { 73 | float: left; 74 | } 75 | .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { 76 | border-radius: 0; 77 | } 78 | .btn-group > .btn-group:first-child { 79 | > .btn:last-child, 80 | > .dropdown-toggle { 81 | @include border-right-radius(0); 82 | } 83 | } 84 | .btn-group > .btn-group:last-child > .btn:first-child { 85 | @include border-left-radius(0); 86 | } 87 | 88 | // On active and open, don't show outline 89 | .btn-group .dropdown-toggle:active, 90 | .btn-group.open .dropdown-toggle { 91 | outline: 0; 92 | } 93 | 94 | 95 | // Sizing 96 | // 97 | // Remix the default button sizing classes into new ones for easier manipulation. 98 | 99 | .btn-group-xs > .btn { @extend .btn-xs; } 100 | .btn-group-sm > .btn { @extend .btn-sm; } 101 | .btn-group-lg > .btn { @extend .btn-lg; } 102 | 103 | 104 | // Split button dropdowns 105 | // ---------------------- 106 | 107 | // Give the line between buttons some depth 108 | .btn-group > .btn + .dropdown-toggle { 109 | padding-left: 8px; 110 | padding-right: 8px; 111 | } 112 | .btn-group > .btn-lg + .dropdown-toggle { 113 | padding-left: 12px; 114 | padding-right: 12px; 115 | } 116 | 117 | // The clickable button for toggling the menu 118 | // Remove the gradient and set the same inset shadow as the :active state 119 | .btn-group.open .dropdown-toggle { 120 | @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 121 | 122 | // Show no shadow for `.btn-link` since it has no other button styles. 123 | &.btn-link { 124 | @include box-shadow(none); 125 | } 126 | } 127 | 128 | 129 | // Reposition the caret 130 | .btn .caret { 131 | margin-left: 0; 132 | } 133 | // Carets in other button sizes 134 | .btn-lg .caret { 135 | border-width: $caret-width-large $caret-width-large 0; 136 | border-bottom-width: 0; 137 | } 138 | // Upside down carets for .dropup 139 | .dropup .btn-lg .caret { 140 | border-width: 0 $caret-width-large $caret-width-large; 141 | } 142 | 143 | 144 | // Vertical button groups 145 | // ---------------------- 146 | 147 | .btn-group-vertical { 148 | > .btn, 149 | > .btn-group, 150 | > .btn-group > .btn { 151 | display: block; 152 | float: none; 153 | width: 100%; 154 | max-width: 100%; 155 | } 156 | 157 | // Clear floats so dropdown menus can be properly placed 158 | > .btn-group { 159 | @include clearfix(); 160 | > .btn { 161 | float: none; 162 | } 163 | } 164 | 165 | > .btn + .btn, 166 | > .btn + .btn-group, 167 | > .btn-group + .btn, 168 | > .btn-group + .btn-group { 169 | margin-top: -1px; 170 | margin-left: 0; 171 | } 172 | } 173 | 174 | .btn-group-vertical > .btn { 175 | &:not(:first-child):not(:last-child) { 176 | border-radius: 0; 177 | } 178 | &:first-child:not(:last-child) { 179 | border-top-right-radius: $border-radius-base; 180 | @include border-bottom-radius(0); 181 | } 182 | &:last-child:not(:first-child) { 183 | border-bottom-left-radius: $border-radius-base; 184 | @include border-top-radius(0); 185 | } 186 | } 187 | .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { 188 | border-radius: 0; 189 | } 190 | .btn-group-vertical > .btn-group:first-child:not(:last-child) { 191 | > .btn:last-child, 192 | > .dropdown-toggle { 193 | @include border-bottom-radius(0); 194 | } 195 | } 196 | .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { 197 | @include border-top-radius(0); 198 | } 199 | 200 | 201 | 202 | // Justified button groups 203 | // ---------------------- 204 | 205 | .btn-group-justified { 206 | display: table; 207 | width: 100%; 208 | table-layout: fixed; 209 | border-collapse: separate; 210 | > .btn, 211 | > .btn-group { 212 | float: none; 213 | display: table-cell; 214 | width: 1%; 215 | } 216 | > .btn-group .btn { 217 | width: 100%; 218 | } 219 | } 220 | 221 | 222 | // Checkbox and radio options 223 | [data-toggle="buttons"] > .btn > input[type="radio"], 224 | [data-toggle="buttons"] > .btn > input[type="checkbox"] { 225 | display: none; 226 | } 227 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_buttons.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Buttons 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // -------------------------------------------------- 8 | 9 | .btn { 10 | display: inline-block; 11 | margin-bottom: 0; // For input.btn 12 | font-weight: $btn-font-weight; 13 | text-align: center; 14 | vertical-align: middle; 15 | cursor: pointer; 16 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 17 | border: 1px solid transparent; 18 | white-space: nowrap; 19 | @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $border-radius-base); 20 | @include user-select(none); 21 | 22 | &, 23 | &:active, 24 | &.active { 25 | &:focus { 26 | @include tab-focus(); 27 | } 28 | } 29 | 30 | &:hover, 31 | &:focus { 32 | color: $btn-default-color; 33 | text-decoration: none; 34 | } 35 | 36 | &:active, 37 | &.active { 38 | outline: 0; 39 | background-image: none; 40 | @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); 41 | } 42 | 43 | &.disabled, 44 | &[disabled], 45 | fieldset[disabled] & { 46 | cursor: not-allowed; 47 | pointer-events: none; // Future-proof disabling of clicks 48 | @include opacity(.65); 49 | @include box-shadow(none); 50 | } 51 | } 52 | 53 | 54 | // Alternate buttons 55 | // -------------------------------------------------- 56 | 57 | .btn-default { 58 | @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); 59 | } 60 | .btn-primary { 61 | @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); 62 | } 63 | // Success appears as green 64 | .btn-success { 65 | @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border); 66 | } 67 | // Info appears as blue-green 68 | .btn-info { 69 | @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border); 70 | } 71 | // Warning appears as orange 72 | .btn-warning { 73 | @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border); 74 | } 75 | // Danger and error appear as red 76 | .btn-danger { 77 | @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border); 78 | } 79 | 80 | 81 | // Link buttons 82 | // ------------------------- 83 | 84 | // Make a button look and behave like a link 85 | .btn-link { 86 | color: $link-color; 87 | font-weight: normal; 88 | cursor: pointer; 89 | border-radius: 0; 90 | 91 | &, 92 | &:active, 93 | &[disabled], 94 | fieldset[disabled] & { 95 | background-color: transparent; 96 | @include box-shadow(none); 97 | } 98 | &, 99 | &:hover, 100 | &:focus, 101 | &:active { 102 | border-color: transparent; 103 | } 104 | &:hover, 105 | &:focus { 106 | color: $link-hover-color; 107 | text-decoration: underline; 108 | background-color: transparent; 109 | } 110 | &[disabled], 111 | fieldset[disabled] & { 112 | &:hover, 113 | &:focus { 114 | color: $btn-link-disabled-color; 115 | text-decoration: none; 116 | } 117 | } 118 | } 119 | 120 | 121 | // Button Sizes 122 | // -------------------------------------------------- 123 | 124 | .btn-lg { 125 | // line-height: ensure even-numbered height of button next to large input 126 | @include button-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $border-radius-large); 127 | } 128 | .btn-sm { 129 | // line-height: ensure proper height of button next to small input 130 | @include button-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $border-radius-small); 131 | } 132 | .btn-xs { 133 | @include button-size($padding-xs-vertical, $padding-xs-horizontal, $font-size-small, $line-height-small, $border-radius-small); 134 | } 135 | 136 | 137 | // Block button 138 | // -------------------------------------------------- 139 | 140 | .btn-block { 141 | display: block; 142 | width: 100%; 143 | padding-left: 0; 144 | padding-right: 0; 145 | } 146 | 147 | // Vertically space out multiple block buttons 148 | .btn-block + .btn-block { 149 | margin-top: 5px; 150 | } 151 | 152 | // Specificity overrides 153 | input[type="submit"], 154 | input[type="reset"], 155 | input[type="button"] { 156 | &.btn-block { 157 | width: 100%; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_carousel.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // -------------------------------------------------- 4 | 5 | 6 | // Wrapper for the slide container and indicators 7 | .carousel { 8 | position: relative; 9 | } 10 | 11 | .carousel-inner { 12 | position: relative; 13 | overflow: hidden; 14 | width: 100%; 15 | 16 | > .item { 17 | display: none; 18 | position: relative; 19 | @include transition(.6s ease-in-out left); 20 | 21 | // Account for jankitude on images 22 | > img, 23 | > a > img { 24 | @include img-responsive(); 25 | line-height: 1; 26 | } 27 | } 28 | 29 | > .active, 30 | > .next, 31 | > .prev { display: block; } 32 | 33 | > .active { 34 | left: 0; 35 | } 36 | 37 | > .next, 38 | > .prev { 39 | position: absolute; 40 | top: 0; 41 | width: 100%; 42 | } 43 | 44 | > .next { 45 | left: 100%; 46 | } 47 | > .prev { 48 | left: -100%; 49 | } 50 | > .next.left, 51 | > .prev.right { 52 | left: 0; 53 | } 54 | 55 | > .active.left { 56 | left: -100%; 57 | } 58 | > .active.right { 59 | left: 100%; 60 | } 61 | 62 | } 63 | 64 | // Left/right controls for nav 65 | // --------------------------- 66 | 67 | .carousel-control { 68 | position: absolute; 69 | top: 0; 70 | left: 0; 71 | bottom: 0; 72 | width: $carousel-control-width; 73 | @include opacity($carousel-control-opacity); 74 | font-size: $carousel-control-font-size; 75 | color: $carousel-control-color; 76 | text-align: center; 77 | text-shadow: $carousel-text-shadow; 78 | // We can't have this transition here because WebKit cancels the carousel 79 | // animation if you trip this while in the middle of another animation. 80 | 81 | // Set gradients for backgrounds 82 | &.left { 83 | @include gradient-horizontal($start-color: rgba(0,0,0,.5), $end-color: rgba(0,0,0,.0001)); 84 | } 85 | &.right { 86 | left: auto; 87 | right: 0; 88 | @include gradient-horizontal($start-color: rgba(0,0,0,.0001), $end-color: rgba(0,0,0,.5)); 89 | } 90 | 91 | // Hover/focus state 92 | &:hover, 93 | &:focus { 94 | outline: none; 95 | color: $carousel-control-color; 96 | text-decoration: none; 97 | @include opacity(.9); 98 | } 99 | 100 | // Toggles 101 | .icon-prev, 102 | .icon-next, 103 | .glyphicon-chevron-left, 104 | .glyphicon-chevron-right { 105 | position: absolute; 106 | top: 50%; 107 | z-index: 5; 108 | display: inline-block; 109 | } 110 | .icon-prev, 111 | .glyphicon-chevron-left { 112 | left: 50%; 113 | } 114 | .icon-next, 115 | .glyphicon-chevron-right { 116 | right: 50%; 117 | } 118 | .icon-prev, 119 | .icon-next { 120 | width: 20px; 121 | height: 20px; 122 | margin-top: -10px; 123 | margin-left: -10px; 124 | font-family: serif; 125 | } 126 | 127 | .icon-prev { 128 | &:before { 129 | content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) 130 | } 131 | } 132 | .icon-next { 133 | &:before { 134 | content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) 135 | } 136 | } 137 | } 138 | 139 | // Optional indicator pips 140 | // 141 | // Add an unordered list with the following class and add a list item for each 142 | // slide your carousel holds. 143 | 144 | .carousel-indicators { 145 | position: absolute; 146 | bottom: 10px; 147 | left: 50%; 148 | z-index: 15; 149 | width: 60%; 150 | margin-left: -30%; 151 | padding-left: 0; 152 | list-style: none; 153 | text-align: center; 154 | 155 | li { 156 | display: inline-block; 157 | width: 10px; 158 | height: 10px; 159 | margin: 1px; 160 | text-indent: -999px; 161 | border: 1px solid $carousel-indicator-border-color; 162 | border-radius: 10px; 163 | cursor: pointer; 164 | 165 | // IE8-9 hack for event handling 166 | // 167 | // Internet Explorer 8-9 does not support clicks on elements without a set 168 | // `background-color`. We cannot use `filter` since that's not viewed as a 169 | // background color by the browser. Thus, a hack is needed. 170 | // 171 | // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we 172 | // set alpha transparency for the best results possible. 173 | background-color: #000 \9; // IE8 174 | background-color: rgba(0,0,0,0); // IE9 175 | } 176 | .active { 177 | margin: 0; 178 | width: 12px; 179 | height: 12px; 180 | background-color: $carousel-indicator-active-bg; 181 | } 182 | } 183 | 184 | // Optional captions 185 | // ----------------------------- 186 | // Hidden by default for smaller viewports 187 | .carousel-caption { 188 | position: absolute; 189 | left: 15%; 190 | right: 15%; 191 | bottom: 20px; 192 | z-index: 10; 193 | padding-top: 20px; 194 | padding-bottom: 20px; 195 | color: $carousel-caption-color; 196 | text-align: center; 197 | text-shadow: $carousel-text-shadow; 198 | & .btn { 199 | text-shadow: none; // No shadow for button elements in carousel-caption 200 | } 201 | } 202 | 203 | 204 | // Scale up controls for tablets and up 205 | @media screen and (min-width: $screen-sm-min) { 206 | 207 | // Scale up the controls a smidge 208 | .carousel-control { 209 | .glyphicon-chevron-left, 210 | .glyphicon-chevron-right, 211 | .icon-prev, 212 | .icon-next { 213 | width: 30px; 214 | height: 30px; 215 | margin-top: -15px; 216 | margin-left: -15px; 217 | font-size: 30px; 218 | } 219 | } 220 | 221 | // Show and left align the captions 222 | .carousel-caption { 223 | left: 20%; 224 | right: 20%; 225 | padding-bottom: 30px; 226 | } 227 | 228 | // Move up the indicators 229 | .carousel-indicators { 230 | bottom: 20px; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_close.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: ($font-size-base * 1.5); 9 | font-weight: $close-font-weight; 10 | line-height: 1; 11 | color: $close-color; 12 | text-shadow: $close-text-shadow; 13 | @include opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: $close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | @include opacity(.5); 21 | } 22 | 23 | // [converter] extracted button& to button.close 24 | } 25 | 26 | // Additional properties for button version 27 | // iOS requires the button element instead of an anchor tag. 28 | // If you want the anchor version, it requires `href="#"`. 29 | button.close { 30 | padding: 0; 31 | cursor: pointer; 32 | background: transparent; 33 | border: 0; 34 | -webkit-appearance: none; 35 | } 36 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_code.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: $font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: $code-color; 19 | background-color: $code-bg; 20 | white-space: nowrap; 21 | border-radius: $border-radius-base; 22 | } 23 | 24 | // User input typically entered via keyboard 25 | kbd { 26 | padding: 2px 4px; 27 | font-size: 90%; 28 | color: $kbd-color; 29 | background-color: $kbd-bg; 30 | border-radius: $border-radius-small; 31 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); 32 | } 33 | 34 | // Blocks of code 35 | pre { 36 | display: block; 37 | padding: (($line-height-computed - 1) / 2); 38 | margin: 0 0 ($line-height-computed / 2); 39 | font-size: ($font-size-base - 1); // 14px to 13px 40 | line-height: $line-height-base; 41 | word-break: break-all; 42 | word-wrap: break-word; 43 | color: $pre-color; 44 | background-color: $pre-bg; 45 | border: 1px solid $pre-border-color; 46 | border-radius: $border-radius-base; 47 | 48 | // Account for some code outputs that place code tags in pre tags 49 | code { 50 | padding: 0; 51 | font-size: inherit; 52 | color: inherit; 53 | white-space: pre-wrap; 54 | background-color: transparent; 55 | border-radius: 0; 56 | } 57 | } 58 | 59 | // Enable scrollable blocks of code 60 | .pre-scrollable { 61 | max-height: $pre-scrollable-max-height; 62 | overflow-y: scroll; 63 | } 64 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_component-animations.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | @include transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | &.in { 21 | display: block; 22 | } 23 | } 24 | .collapsing { 25 | position: relative; 26 | height: 0; 27 | overflow: hidden; 28 | @include transition(height .35s ease); 29 | } 30 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_dropdowns.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dropdown menus 3 | // -------------------------------------------------- 4 | 5 | 6 | // Dropdown arrow/caret 7 | .caret { 8 | display: inline-block; 9 | width: 0; 10 | height: 0; 11 | margin-left: 2px; 12 | vertical-align: middle; 13 | border-top: $caret-width-base solid; 14 | border-right: $caret-width-base solid transparent; 15 | border-left: $caret-width-base solid transparent; 16 | } 17 | 18 | // The dropdown wrapper (div) 19 | .dropdown { 20 | position: relative; 21 | } 22 | 23 | // Prevent the focus on the dropdown toggle when closing dropdowns 24 | .dropdown-toggle:focus { 25 | outline: 0; 26 | } 27 | 28 | // The dropdown menu (ul) 29 | .dropdown-menu { 30 | position: absolute; 31 | top: 100%; 32 | left: 0; 33 | z-index: $zindex-dropdown; 34 | display: none; // none by default, but block on "open" of the menu 35 | float: left; 36 | min-width: 160px; 37 | padding: 5px 0; 38 | margin: 2px 0 0; // override default ul 39 | list-style: none; 40 | font-size: $font-size-base; 41 | background-color: $dropdown-bg; 42 | border: 1px solid $dropdown-fallback-border; // IE8 fallback 43 | border: 1px solid $dropdown-border; 44 | border-radius: $border-radius-base; 45 | @include box-shadow(0 6px 12px rgba(0,0,0,.175)); 46 | background-clip: padding-box; 47 | 48 | // Aligns the dropdown menu to right 49 | // 50 | // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` 51 | &.pull-right { 52 | right: 0; 53 | left: auto; 54 | } 55 | 56 | // Dividers (basically an hr) within the dropdown 57 | .divider { 58 | @include nav-divider($dropdown-divider-bg); 59 | } 60 | 61 | // Links within the dropdown menu 62 | > li > a { 63 | display: block; 64 | padding: 3px 20px; 65 | clear: both; 66 | font-weight: normal; 67 | line-height: $line-height-base; 68 | color: $dropdown-link-color; 69 | white-space: nowrap; // prevent links from randomly breaking onto new lines 70 | } 71 | } 72 | 73 | // Hover/Focus state 74 | .dropdown-menu > li > a { 75 | &:hover, 76 | &:focus { 77 | text-decoration: none; 78 | color: $dropdown-link-hover-color; 79 | background-color: $dropdown-link-hover-bg; 80 | } 81 | } 82 | 83 | // Active state 84 | .dropdown-menu > .active > a { 85 | &, 86 | &:hover, 87 | &:focus { 88 | color: $dropdown-link-active-color; 89 | text-decoration: none; 90 | outline: 0; 91 | background-color: $dropdown-link-active-bg; 92 | } 93 | } 94 | 95 | // Disabled state 96 | // 97 | // Gray out text and ensure the hover/focus state remains gray 98 | 99 | .dropdown-menu > .disabled > a { 100 | &, 101 | &:hover, 102 | &:focus { 103 | color: $dropdown-link-disabled-color; 104 | } 105 | } 106 | // Nuke hover/focus effects 107 | .dropdown-menu > .disabled > a { 108 | &:hover, 109 | &:focus { 110 | text-decoration: none; 111 | background-color: transparent; 112 | background-image: none; // Remove CSS gradient 113 | @include reset-filter(); 114 | cursor: not-allowed; 115 | } 116 | } 117 | 118 | // Open state for the dropdown 119 | .open { 120 | // Show the menu 121 | > .dropdown-menu { 122 | display: block; 123 | } 124 | 125 | // Remove the outline when :focus is triggered 126 | > a { 127 | outline: 0; 128 | } 129 | } 130 | 131 | // Menu positioning 132 | // 133 | // Add extra class to `.dropdown-menu` to flip the alignment of the dropdown 134 | // menu with the parent. 135 | .dropdown-menu-right { 136 | left: auto; // Reset the default from `.dropdown-menu` 137 | right: 0; 138 | } 139 | // With v3, we enabled auto-flipping if you have a dropdown within a right 140 | // aligned nav component. To enable the undoing of that, we provide an override 141 | // to restore the default dropdown menu alignment. 142 | // 143 | // This is only for left-aligning a dropdown menu within a `.navbar-right` or 144 | // `.pull-right` nav component. 145 | .dropdown-menu-left { 146 | left: 0; 147 | right: auto; 148 | } 149 | 150 | // Dropdown section headers 151 | .dropdown-header { 152 | display: block; 153 | padding: 3px 20px; 154 | font-size: $font-size-small; 155 | line-height: $line-height-base; 156 | color: $dropdown-header-color; 157 | } 158 | 159 | // Backdrop to catch body clicks on mobile, etc. 160 | .dropdown-backdrop { 161 | position: fixed; 162 | left: 0; 163 | right: 0; 164 | bottom: 0; 165 | top: 0; 166 | z-index: ($zindex-dropdown - 10); 167 | } 168 | 169 | // Right aligned dropdowns 170 | .pull-right > .dropdown-menu { 171 | right: 0; 172 | left: auto; 173 | } 174 | 175 | // Allow for dropdowns to go bottom up (aka, dropup-menu) 176 | // 177 | // Just add .dropup after the standard .dropdown class and you're set, bro. 178 | // TODO: abstract this so that the navbar fixed styles are not placed here? 179 | 180 | .dropup, 181 | .navbar-fixed-bottom .dropdown { 182 | // Reverse the caret 183 | .caret { 184 | border-top: 0; 185 | border-bottom: $caret-width-base solid; 186 | content: ""; 187 | } 188 | // Different positioning for bottom up menu 189 | .dropdown-menu { 190 | top: auto; 191 | bottom: 100%; 192 | margin-bottom: 1px; 193 | } 194 | } 195 | 196 | 197 | // Component alignment 198 | // 199 | // Reiterate per navbar.less and the modified component alignment there. 200 | 201 | @media (min-width: $grid-float-breakpoint) { 202 | .navbar-right { 203 | .dropdown-menu { 204 | right: 0; left: auto; 205 | } 206 | // Necessary for overrides of the default right aligned menu. 207 | // Will remove come v4 in all likelihood. 208 | .dropdown-menu-left { 209 | left: 0; right: auto; 210 | } 211 | } 212 | } 213 | 214 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_grid.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container widths 7 | // 8 | // Set the container width, and override it for fixed navbars in media queries. 9 | 10 | .container { 11 | @include container-fixed(); 12 | 13 | @media (min-width: $screen-sm-min) { 14 | width: $container-sm; 15 | } 16 | @media (min-width: $screen-md-min) { 17 | width: $container-md; 18 | } 19 | @media (min-width: $screen-lg-min) { 20 | width: $container-lg; 21 | } 22 | } 23 | 24 | 25 | // Fluid container 26 | // 27 | // Utilizes the mixin meant for fixed width containers, but without any defined 28 | // width for fluid, full width layouts. 29 | 30 | .container-fluid { 31 | @include container-fixed(); 32 | } 33 | 34 | 35 | // Row 36 | // 37 | // Rows contain and clear the floats of your columns. 38 | 39 | .row { 40 | @include make-row(); 41 | } 42 | 43 | 44 | // Columns 45 | // 46 | // Common styles for small and large grid columns 47 | 48 | @include make-grid-columns(); 49 | 50 | 51 | // Extra small grid 52 | // 53 | // Columns, offsets, pushes, and pulls for extra small devices like 54 | // smartphones. 55 | 56 | @include make-grid(xs); 57 | 58 | 59 | // Small grid 60 | // 61 | // Columns, offsets, pushes, and pulls for the small device range, from phones 62 | // to tablets. 63 | 64 | @media (min-width: $screen-sm-min) { 65 | @include make-grid(sm); 66 | } 67 | 68 | 69 | // Medium grid 70 | // 71 | // Columns, offsets, pushes, and pulls for the desktop device range. 72 | 73 | @media (min-width: $screen-md-min) { 74 | @include make-grid(md); 75 | } 76 | 77 | 78 | // Large grid 79 | // 80 | // Columns, offsets, pushes, and pulls for the large desktop device range. 81 | 82 | @media (min-width: $screen-lg-min) { 83 | @include make-grid(lg); 84 | } 85 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_input-groups.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Input groups 3 | // -------------------------------------------------- 4 | 5 | // Base styles 6 | // ------------------------- 7 | .input-group { 8 | position: relative; // For dropdowns 9 | display: table; 10 | border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table 11 | 12 | // Undo padding and float of grid classes 13 | &[class*="col-"] { 14 | float: none; 15 | padding-left: 0; 16 | padding-right: 0; 17 | } 18 | 19 | .form-control { 20 | // Ensure that the input is always above the *appended* addon button for 21 | // proper border colors. 22 | position: relative; 23 | z-index: 2; 24 | 25 | // IE9 fubars the placeholder attribute in text inputs and the arrows on 26 | // select elements in input groups. To fix it, we float the input. Details: 27 | // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855 28 | float: left; 29 | 30 | width: 100%; 31 | margin-bottom: 0; 32 | } 33 | } 34 | 35 | // Sizing options 36 | // 37 | // Remix the default form control sizing classes into new ones for easier 38 | // manipulation. 39 | 40 | .input-group-lg > .form-control, 41 | .input-group-lg > .input-group-addon, 42 | .input-group-lg > .input-group-btn > .btn { @extend .input-lg; } 43 | .input-group-sm > .form-control, 44 | .input-group-sm > .input-group-addon, 45 | .input-group-sm > .input-group-btn > .btn { @extend .input-sm; } 46 | 47 | 48 | // Display as table-cell 49 | // ------------------------- 50 | .input-group-addon, 51 | .input-group-btn, 52 | .input-group .form-control { 53 | display: table-cell; 54 | 55 | &:not(:first-child):not(:last-child) { 56 | border-radius: 0; 57 | } 58 | } 59 | // Addon and addon wrapper for buttons 60 | .input-group-addon, 61 | .input-group-btn { 62 | width: 1%; 63 | white-space: nowrap; 64 | vertical-align: middle; // Match the inputs 65 | } 66 | 67 | // Text input groups 68 | // ------------------------- 69 | .input-group-addon { 70 | padding: $padding-base-vertical $padding-base-horizontal; 71 | font-size: $font-size-base; 72 | font-weight: normal; 73 | line-height: 1; 74 | color: $input-color; 75 | text-align: center; 76 | background-color: $input-group-addon-bg; 77 | border: 1px solid $input-group-addon-border-color; 78 | border-radius: $border-radius-base; 79 | 80 | // Sizing 81 | &.input-sm { 82 | padding: $padding-small-vertical $padding-small-horizontal; 83 | font-size: $font-size-small; 84 | border-radius: $border-radius-small; 85 | } 86 | &.input-lg { 87 | padding: $padding-large-vertical $padding-large-horizontal; 88 | font-size: $font-size-large; 89 | border-radius: $border-radius-large; 90 | } 91 | 92 | // Nuke default margins from checkboxes and radios to vertically center within. 93 | input[type="radio"], 94 | input[type="checkbox"] { 95 | margin-top: 0; 96 | } 97 | } 98 | 99 | // Reset rounded corners 100 | .input-group .form-control:first-child, 101 | .input-group-addon:first-child, 102 | .input-group-btn:first-child > .btn, 103 | .input-group-btn:first-child > .btn-group > .btn, 104 | .input-group-btn:first-child > .dropdown-toggle, 105 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), 106 | .input-group-btn:last-child > .btn-group:not(:last-child) > .btn { 107 | @include border-right-radius(0); 108 | } 109 | .input-group-addon:first-child { 110 | border-right: 0; 111 | } 112 | .input-group .form-control:last-child, 113 | .input-group-addon:last-child, 114 | .input-group-btn:last-child > .btn, 115 | .input-group-btn:last-child > .btn-group > .btn, 116 | .input-group-btn:last-child > .dropdown-toggle, 117 | .input-group-btn:first-child > .btn:not(:first-child), 118 | .input-group-btn:first-child > .btn-group:not(:first-child) > .btn { 119 | @include border-left-radius(0); 120 | } 121 | .input-group-addon:last-child { 122 | border-left: 0; 123 | } 124 | 125 | // Button input groups 126 | // ------------------------- 127 | .input-group-btn { 128 | position: relative; 129 | // Jankily prevent input button groups from wrapping with `white-space` and 130 | // `font-size` in combination with `inline-block` on buttons. 131 | font-size: 0; 132 | white-space: nowrap; 133 | 134 | // Negative margin for spacing, position for bringing hovered/focused/actived 135 | // element above the siblings. 136 | > .btn { 137 | position: relative; 138 | + .btn { 139 | margin-left: -1px; 140 | } 141 | // Bring the "active" button to the front 142 | &:hover, 143 | &:focus, 144 | &:active { 145 | z-index: 2; 146 | } 147 | } 148 | 149 | // Negative margin to only have a 1px border between the two 150 | &:first-child { 151 | > .btn, 152 | > .btn-group { 153 | margin-right: -1px; 154 | } 155 | } 156 | &:last-child { 157 | > .btn, 158 | > .btn-group { 159 | margin-left: -1px; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_jumbotron.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: $jumbotron-padding; 8 | margin-bottom: $jumbotron-padding; 9 | color: $jumbotron-color; 10 | background-color: $jumbotron-bg; 11 | 12 | h1, 13 | .h1 { 14 | color: $jumbotron-heading-color; 15 | } 16 | p { 17 | margin-bottom: ($jumbotron-padding / 2); 18 | font-size: $jumbotron-font-size; 19 | font-weight: 200; 20 | } 21 | 22 | .container & { 23 | border-radius: $border-radius-large; // Only round corners at higher resolutions if contained in a container 24 | } 25 | 26 | .container { 27 | max-width: 100%; 28 | } 29 | 30 | @media screen and (min-width: $screen-sm-min) { 31 | padding-top: ($jumbotron-padding * 1.6); 32 | padding-bottom: ($jumbotron-padding * 1.6); 33 | 34 | .container & { 35 | padding-left: ($jumbotron-padding * 2); 36 | padding-right: ($jumbotron-padding * 2); 37 | } 38 | 39 | h1, 40 | .h1 { 41 | font-size: ($font-size-base * 4.5); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_labels.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: $label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // Add hover effects, but only for links 18 | &[href] { 19 | &:hover, 20 | &:focus { 21 | color: $label-link-hover-color; 22 | text-decoration: none; 23 | cursor: pointer; 24 | } 25 | } 26 | 27 | // Empty labels collapse automatically (not available in IE8) 28 | &:empty { 29 | display: none; 30 | } 31 | 32 | // Quick fix for labels in buttons 33 | .btn & { 34 | position: relative; 35 | top: -1px; 36 | } 37 | } 38 | 39 | // Colors 40 | // Contextual variations (linked labels get darker on :hover) 41 | 42 | .label-default { 43 | @include label-variant($label-default-bg); 44 | } 45 | 46 | .label-primary { 47 | @include label-variant($label-primary-bg); 48 | } 49 | 50 | .label-success { 51 | @include label-variant($label-success-bg); 52 | } 53 | 54 | .label-info { 55 | @include label-variant($label-info-bg); 56 | } 57 | 58 | .label-warning { 59 | @include label-variant($label-warning-bg); 60 | } 61 | 62 | .label-danger { 63 | @include label-variant($label-danger-bg); 64 | } 65 | -------------------------------------------------------------------------------- /styles/lib/bootstrap/_list-group.scss: -------------------------------------------------------------------------------- 1 | // 2 | // List groups 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | // 8 | // Easily usable on