個人プロジェクト用フロントエンドテンプレート

ちょいちょいページを作らなきゃいけないときにいちいち書くのは面倒くさいので
以下のテンプレをFolkしてきて作ってる。都度更新
今のところは基本的にjQueryは使ってない。
基本共同作業もないような小さい物が多いので自分で使いやすい、メンテしやすい、使っててテンション上がる。にフォーカスしてる。

## Directory Tree
- app/
    - public/
- source/
    - coffee/
        - main.coffee
        - helper.coffee
    - jade/
        - index.jade
    - javascript/
        - googleanalytics.js
    - resources/
    - stylus/
        - main.styus
- .gitignore
- bower.json
- gulp.coffee
- gulp.js
- package.json

## .gitignore
時々更新する。なんでpublic以下をワイルドカードじゃなくて一個づつ指定しているのかは理由はあったと思うけど忘れた。
多分時々手でぶち込むRubyのファイルとかがあるからだったと思う。api/とか

```lang:.gitignore
/node_modules
/dev
.DS_Store
/bower_components
Icon\r
Icon
*.log
/source/.tmp
/app/public/resources
/app/public/scripts
/app/public/styles
/app/public/*.html
/app/public/vendors
```

## bower.json
手更新はなし。以下コマンドで。

```
bower init
bower install --save normalize.css
```

## package.json
もう中身いっぱいなのでそのままコピペ
割りと使ってないのも残ってるから時々整理してる
これも手更新はなく、更新する際は npm install --save-dev
で更新してる。（基本開発用のNPMしか読み込まないのでいつも-dev）

```json:package.json
{
  "name": "都度書き換え",
  "version": "0.0.0",
  "description": "都度書き換え",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "node node_modules/.bin/gulp compile"
  },
  "author": "都度書き換え",
  "license": "ISC",
  "homepage": "都度書き換え",
  "devDependencies": {
    "browserify": "^8.0.3",
    "coffee-script": "^1.8.0",
    "coffeeify": "^1.0.0",
    "gulp": "^3.8.10",
    "gulp-coffee": "^2.2.0",
    "gulp-concat": "^2.4.3",
    "gulp-filter": "^3.0.1",
    "gulp-imagemin": "^2.1.0",
    "gulp-jade": "^0.10.0",
    "gulp-livereload": "^3.8.0",
    "gulp-minify-css": "^0.3.11",
    "gulp-notify": "^2.1.0",
    "gulp-pleeease": "^1.2.0",
    "gulp-plumber": "^0.6.6",
    "gulp-rename": "^1.2.0",
    "gulp-sourcemaps": "^1.5.2",
    "gulp-streamify": "0.0.5",
    "gulp-stylus": "^1.3.4",
    "gulp-uglify": "^1.0.2",
    "gulp-watch": "^3.0.0",
    "gulp-webserver": "^0.9.1",
    "vinyl-buffer": "^1.0.0",
    "vinyl-source-stream": "^1.0.0"
  }
}
```

## gulp.js
gulp.coffeeを呼び出すだけ。設定はgulp.coffeeに書く

```js:gulp.js
require('coffee-script/register');
require('./gulpfile.coffee');
```

## gulp.coffee
そこそこ長いけどファイル分割はしてない。一回やったけど逆に面倒クセェな、と思って。
そろそろ整理しないと付け足し付け足しでやってて、使ってないのに読み込んでるのとかありそう。
サーバサイドが必要なときはRuby+Sinatraで書く。その時はWebserver/Livereloadを消す。

```
$ gulp
```
でファイル監視、ビルド、サーバ起動

```coffeescript:gulp.coffee
gulp       = require 'gulp'
jade       = require 'gulp-jade'
coffee     = require 'gulp-coffee'
uglify     = require 'gulp-uglify'
stylus     = require 'gulp-stylus'
minify     = require 'gulp-minify-css'
concat     = require 'gulp-concat'
watch      = require 'gulp-watch'
server     = require 'gulp-webserver'
livereload = require 'gulp-livereload'
plumber    = require 'gulp-plumber'
notify     = require 'gulp-notify'
imagemin   = require 'gulp-imagemin'
rename     = require 'gulp-rename'
buffer     = require 'vinyl-buffer'
filter     = require 'gulp-filter'

browserify = require 'browserify'
coffeeify  = require 'coffeeify'
source     = require 'vinyl-source-stream'
streamify  = require 'gulp-streamify'
sourcemaps = require 'gulp-sourcemaps'
pleeease   = require 'gulp-pleeease'

pub_dir    = 'app/public'

srcdata = {
  'coffee'   : 'source/coffee/**/*.coffee'
  'stylus'   : 'source/stylus/**/*.stylus'
  'jade'     : 'source/jade/**/*.jade'
  'image'    : 'source/resources/**/*'
  'bower'    : ['dev/vendors/**/*','bower_components/**/*']
  'vendorjs' : 'source/javascript/**/*'
}

gulp.task 'compile-js', () ->
  compileFileName = 'application.min.js'
  browserify(
    entries: ['./source/coffee/main.coffee']
    extensions: ['.coffee']
    )
    .transform 'coffeeify'
    .bundle()
    .pipe plumber(errorHandler: notify.onError '<%= error.message %>')
    .pipe source 'application.min.js'
    .pipe buffer()
    .pipe sourcemaps.init
      loadMaps: true
    .pipe streamify uglify({mangle: false})
    .pipe sourcemaps.write(pub_dir+'/scripts')
    .pipe gulp.dest pub_dir+'/scripts'

gulp.task 'compile-css', () ->
  compileFileName = 'application.min.css'
  gulp.src srcdata.stylus
    .pipe plumber(errorHandler: notify.onError '<%= error.message %>')
    .pipe sourcemaps.init()
    .pipe stylus()
    .pipe concat(compileFileName)
    .pipe pleeease
      fallbacks:
        autoprefixer: ['last 4 versions']
      optimizers:
        minifier: false
#    .pipe minify()
    .pipe sourcemaps.write(pub_dir+'/styles')
    .pipe gulp.dest(pub_dir+'/styles')

gulp.task 'compile-html', () ->
  gulp.src srcdata.jade
    .pipe plumber(errorHandler: notify.onError '<%= error.message %>')
    .pipe jade
      pretty: true
    .pipe rename extname: '.html'
    .pipe gulp.dest(pub_dir)

gulp.task 'compile-image', () ->
  gulp.src srcdata.image
    .pipe imagemin()
    .pipe gulp.dest(pub_dir+'/resources')

gulp.task 'move-vendors', () ->
  cssFilter = filter '**/*.css'
  gulp.src srcdata.bower
    #.pipe cssFilter
    .pipe gulp.dest(pub_dir+'/vendors')

gulp.task 'move-vendorjs', () ->
  gulp.src srcdata.vendorjs
    .pipe gulp.dest(pub_dir+'/scripts/vendors')

gulp.task 'webserver', () ->
  gulp.src "app/public"
    .pipe server(livereload:true)

gulp.task 'compile', [
  'compile-js'
  'compile-css'
  'compile-html'
  'move-vendors'
  'move-vendorjs'
  'compile-image'
]
gulp.task 'watch', () ->
  gulp.watch srcdata.stylus, ['compile-css']
  gulp.watch srcdata.coffee, ['compile-js']
  gulp.watch srcdata.jade, ['compile-html']
  gulp.watch srcdata.image, ['compile-image']
  gulp.watch srcdata.bower, ['move-vendors']
  gulp.watch srcdata.vendorjs, ['move-vendorjs']

gulp.task 'default', [
  'compile'
  'watch'
  'webserver'
]
```

## source/coffee/main.coffee

Coffeeの親玉こいつでいろんなの呼び出す（最終的にbrowserifyで結合する）
追加する際は基本1クラスずつ1coffeeファイルを作成してmain.coffee上で呼び出して展開する。
最終的にはすべてのCofeeは一つにまとめられてminifyされ /app/public/scripts/appplication.min.js が爆誕する。

```coffeescript:main.coffee
# helper class
Helper = require './helper'
window.helper = new Helper()
```

## source/coffee/helper.coffee
大体使う便利なやつらを入れておく雑多クラスファイル
クラスファイル作るときはだいたいこれをコピペして下地にしてる

```coffeescript:helper.coffee
class Helper

  constructor: (args)->

  $id: (id) ->
    return document.getElementById id

  xhr: (url,cb, opt) ->
    xhr = new XMLHttpRequest()
    xhr.open 'GET', url

    xhr.onreadystatechange = () ->
      if xhr.readyState == 4 && xhr.status == 200
        cb xhr.responseText,opt
      else if xhr.readyState == 4 && xhr.status != 200
        error xhr.status,opt
      return this

    xhr.send()
    return this

  htmlize: (str) ->
    str = str.replace /(https?:\/\/[\x21-\x7e]+)/gi, "<a href=$1>$1</a>"
    str = str.replace /&/gi,'&amp;'
    str = str.replace /\"/gi, '&quote'
    str = str.replace /\n/gi,'<br>'
    return str

  formatDate: (date, format) ->
    if format is no
      format = 'YYYY-MM-DD hh:mm:ss.SSS'

    format = format.replace /YYYY/g, date.getFullYear()
    format = format.replace /MM/g, ('0' + (date.getMonth() + 1)).slice(-2)
    format = format.replace /DD/g, ('0' + date.getDate()).slice(-2)
    format = format.replace /hh/g, ('0' + date.getHours()).slice(-2)
    format = format.replace /mm/g, ('0' + date.getMinutes()).slice(-2)
    format = format.replace /ss/g, ('0' + date.getSeconds()).slice(-2)

    if format.match(/S/g) is yes
      milliSeconds = ('00' + date.getMilliseconds()).slice(-3)
      length = format.match(/S/g).length
      for i in length
        format = format.replace /S/, milliSeconds.substring(i, i + 1)

    return format

  setLoading: ($target) ->
    $loading = document.createElement "div"
    $loading.classList.add "spinner"
    bounce1 = '<div class="bounce1"></div>'
    bounce2 = '<div class="bounce2"></div>'
    bounce3 = '<div class="bounce3"></div>'
    $loading.innerHTML = bounce1+bounce2+bounce3
    $target.appendChild $loading
    return $loading

  removeLoading: ($target) ->
    $parent = $target.parentNode
    $parent.removeChild $target

module.exports = Helper
```

## source/stylus/main.stylus
CSS的な奴は基本Stylusを採用。理由は好みだから。
JSとセットで使うようなスタイルはファイルを分けて例えば"slider.stylus"とかに気分でする
最終的にすべて結合されてminifyされ /app/public/styles/application.min.css が爆誕する。

基本表記ルールは普通。
- コロン後半角スペース一つ
- 行閉じセミコロンなし
- pleeeaseによってベンダープリフィクスは自動でつくのでつけない

```
selecta
  property: value
  property: value
```

```sass:main.stylus

// ------------------------------
// Vars
// ------------------------------
ColorMainText = #333
ColorSubText  = #555
ColorAntiText = #fefefe
ColorMainBackground = #fefefe
ColorWeekText = #aaa
ColorMainShadow = #aaa
ColorActiveShadow = #ccc

SizeMainFont = 92%

// ------------------------------
// Basics
// ------------------------------

body
  background: ColorMainBackground
  color: ColorMainText
  font-size: SizeMainFont
  font-family: Helvetica Neue, Helvetica, Arial, sans-serif

h1
  font-size: 2rem
  margin: 0
  padding: 1.2rem 0

h2
  font-size: 1.6rem
  margin: 0 0 6px

h3
  font-size: 1.2rem



// ------------------------------
// Layouts
// ------------------------------
#container
  margin: 0 10px
  padding: 0


@media(max-width: 720px)
  #container
    margin: 0

@media print
  form.searchbox, .sharedaddy, #navigation
    display: none

/*
utility
*/
.spinner
  width: 54px
  height: 18px
  margin: 20px auto
  div
    width: 18px
    height: 18px
    background-color: rgba(124,124,124,0.4)
    filter: blur(1px)

    border-radius: 100%
    display: inline-block
    animation: bouncedelay 1.4s infinite ease-in-out
    animation-fill-mode: both
  .bounce1
    animation-delay: -0.32s
  .bounce2
    animation-delay: -0.16s
  .bounce3
    animation-delay: -0.08s

.clearbox
  &:after
    content: ""
    display: block
    clear: both

/* animation */
@keyframes bouncedelay
  0%, 80%, 100%
    transform: scale(0.0)
   40%
    transform: scale(1.0)

```

## source/jade/index.jade
HTMLテンプレート、これが一番毎回ゼロから書くのだるい
基本的にはJADEを採用、なぜならHTML書くより楽だしCoffeeやStylusとほぼ同じ感じでかけるから（インデント判別、セミコロン不要 等）
font-awesome読み込んでるけどほとんど使ってないな、、、現状、、、

```jade:index.jade
- meta_title = '都度書き換え'
- meta_description = '都度書き換え'
- author = '都度書き換え'

doctype html
html
  head
    meta(charset='UTF-8')
    title #{meta_title}
    meta(name='description', content=meta_description)
    meta(name="viewport", content="width=device-width, initial-scale=1")
    link(rel="stylesheet",href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css")
    link(href="/vendors/normalize.css/normalize.css",rel="stylesheet",type="text/css")
    link(rel="stylesheet", href='http://fonts.googleapis.com/css?family=Roboto:900,400',type='text/css')
    link(href="/styles/application.min.css",rel="stylesheet",type="text/css")
    link(rel="shortcut icon", href="/resources/favicon.ico")

  body
    div#container



    p.copyrights copyrights&copy;#{author}

    script(type="text/javascript",src="/scripts/application.min.js",charset='UTF-8')
    script(type="text/javascript",src="/scripts/vendors/googleanalytics.js",charset='UTF-8')

```

## source/javascript/googleanalytics.js
サードパーティのJavascriptとかはそのまま結合とかもせず移すので source/javascript/ に入れる
デフォルトではGoogleAnalyticsのコードだけ入ってる

```js:googleanalytics.js
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', '都度書き換え', 'auto');
ga('send', 'pageview');

```

## source/resources/

画像は基本的に最適化されて /app/public/resources/** 以下にディレクトリ構造を保って移動される