├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── f6.js ├── package.json ├── server.js └── web ├── ex1-dom ├── main.html ├── plugin.html └── plugin.js ├── ex2-router └── main.html ├── ex3-blogInClient ├── main.html └── main.js ├── ex4-blogInServer └── server.js ├── ex5-blogInAjax ├── main.html ├── main.js └── server.js ├── ex6-upload ├── README.md ├── server.js ├── upload.html └── upload │ └── empty.txt ├── ex7-uploadAjax ├── README.md ├── server.js ├── upload.html └── upload │ └── empty.txt ├── ex8-blogInAjaxMongodb ├── main.html ├── main.js └── server.js └── f6.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # exe 6 | *.exe 7 | 8 | # bak 9 | *.bak 10 | bak 11 | 12 | # node 13 | node_modules 14 | 15 | # database 16 | db 17 | 18 | # mac 19 | .DS_Store 20 | 21 | # Runtime data 22 | pids 23 | *.pid 24 | *.seed 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (http://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directory 42 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 43 | node_modules 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | - "6" 5 | - "5" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 陳鍾誠 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # f6 -- ES6 Front-End Framework 2 | 3 | ## Install 4 | 5 | For node.js 6 | 7 | ``` 8 | $ npm install f6 9 | ``` 10 | 11 | For browser, download the [f6.js](web/f6.js) file and link into your html. 12 | 13 | 14 | ## Demo 15 | 16 | ``` 17 | $ git clone https://github.com/ccckmit/f6.git 18 | $ cd f6 19 | $ npm install --dev 20 | $ npm start 21 | ``` 22 | 23 | The server started at `http://localhost:3000/` , visiting the following page for demo program. 24 | 25 | ## Example 1 26 | * [Example 1 : DOM](web/ex1-dom/) : `http://localhost:3000/ex1-dom/main.html` 27 | 28 | ## Example 2 29 | * [Example 2 : router](web/ex2-router/) : `http://localhost:3000/ex2-router/main.html` 30 | 31 | ## Example 3 32 | * [Example 3 : blogInClient](web/ex3-blogInClient/) : `http://localhost:3000/ex3-blogInClient/main.html` 33 | 34 | For the other demo, you should start the server inside the subfolder 35 | 36 | ## Example 4 37 | 38 | ``` 39 | $ cd web/ex4-blogInServer 40 | $ node server 41 | ``` 42 | 43 | * [Example 4 : blogInServer](web/ex4-blogInServer/) : `http://localhost:3000/ 44 | 45 | ## Example 5 46 | 47 | ``` 48 | $ cd web/ex5-blogInAjax 49 | $ node server 50 | ``` 51 | 52 | * [Example 5 : blogInAjax](web/ex5-blogInAjax/) : `http://localhost:3000/ex5-blogInAjax/main.html` 53 | 54 | ## Example 6 55 | 56 | ``` 57 | $ cd web/ex6-upload 58 | $ node server 59 | ``` 60 | 61 | * [Example 6 : upload](web/ex6-upload/) : `http://localhost:3000/ex6-upload/upload.html` 62 | 63 | ## Example 7 64 | 65 | ``` 66 | $ cd web/ex7-uploadAjax 67 | $ node server 68 | ``` 69 | 70 | * [Example 7 : uploadAjax](web/ex7-uploadAjax/) : `http://localhost:3000/ex7-uploadAjax/upload.html` 71 | 72 | ## Example 8 73 | 74 | ``` 75 | $ cd web/ex8-blogInAjaxMongodb 76 | $ node server 77 | ``` 78 | 79 | * [Example 8 : blogInAjaxMongodb](web/ex8-blogInAjaxMongodb/) : `http://localhost:3000/ex8-blogInAjaxMongodb/main.html` -------------------------------------------------------------------------------- /f6.js: -------------------------------------------------------------------------------- 1 | f6 = module.exports = { 2 | scriptLoaded: {}, 3 | router: { map: new Map() } 4 | } 5 | 6 | // onhashchange => route 7 | f6.route = function (regexp, f) { 8 | f6.router.map.set(regexp, f) 9 | return this 10 | } 11 | 12 | f6.onhash = function () { 13 | var promise = new Promise(function (resolve, reject) { 14 | var hash = window.location.hash.trim().substring(1) 15 | var m 16 | for (let [regexp, f] of f6.router.map) { 17 | m = hash.match(regexp) 18 | if (m) { 19 | f(m, hash) 20 | resolve(m) 21 | break 22 | } 23 | } 24 | if (!m) reject(new Error('no route match hash')) 25 | }) 26 | return promise 27 | } 28 | 29 | window.onhashchange = function () { 30 | f6.onhash() 31 | } 32 | 33 | f6.go = function (hash) { 34 | window.location.hash = '#' + hash 35 | return f6.onhash() 36 | // return this 37 | } 38 | 39 | // DOM Element 40 | Element.prototype.one = function (selector) { 41 | return this.querySelector(selector) 42 | } 43 | 44 | Element.prototype.all = function (selector) { 45 | return this.querySelectorAll(selector) 46 | } 47 | 48 | Element.prototype.hide = function () { 49 | this.hidden = true 50 | return this 51 | } 52 | 53 | Element.prototype.show = function () { 54 | this.hidden = undefined 55 | return this 56 | } 57 | 58 | Element.prototype.html = function (html) { 59 | this.innerHTML = html 60 | return this 61 | } 62 | 63 | // NodeList 64 | NodeList.prototype.each = function (f) { 65 | this.forEach(f) 66 | return this 67 | } 68 | 69 | NodeList.prototype.hide = function () { 70 | this.each(function (x) { x.hide() }) 71 | return this 72 | } 73 | 74 | NodeList.prototype.show = function () { 75 | this.each(function (x) { x.show() }) 76 | return this 77 | } 78 | 79 | NodeList.prototype.html = function (html) { 80 | this.each(function (x) { x.html(html) }) 81 | return this 82 | } 83 | 84 | // ==> not test yet 85 | f6.nodes = function (html) { 86 | var div = document.createElement('div') 87 | div.innerHTML = html 88 | var childNodes = div.childNodes 89 | delete div 90 | return childNodes 91 | } 92 | 93 | NodeList.prototype.unshift = function (html) { 94 | var nodes = f6.nodes(html) 95 | for (var i in div.childNodes) { 96 | this.insertBefore(nodes[i], this.firstChild) 97 | } 98 | return this 99 | } 100 | 101 | NodeList.prototype.push = function (html) { 102 | var nodes = f6.nodes(html) 103 | for (var i in div.childNodes) { 104 | this.appendChild(nodes[i]) 105 | } 106 | return this 107 | } 108 | 109 | // DOM short cut 110 | f6.one = function (selector) { 111 | return document.querySelector(selector) 112 | } 113 | 114 | f6.all = function (selector) { 115 | return document.querySelectorAll(selector) 116 | } 117 | 118 | // View : Event Handling 119 | f6.on = function (obj, event, f) { 120 | var o = (typeof obj === 'string') ? f6.one(obj) : obj 121 | o.addEventListener(event, f) 122 | } 123 | 124 | // load stylesheet (CSS) 125 | f6.styleLoad = function (url) { 126 | var ss = document.createElement('link') 127 | ss.type = 'text/css' 128 | ss.rel = 'stylesheet' 129 | ss.href = url 130 | f6.one('head').appendChild(ss) 131 | } 132 | 133 | // load script (JS) 134 | f6.scriptLoad = function (url) { 135 | return new Promise(function (resolve, reject) { 136 | var urlLoaded = f6.scriptLoaded[url] 137 | if (urlLoaded === true) resolve(url) 138 | var script = document.createElement('script') 139 | script.onload = function () { 140 | f6.scriptLoaded[url] = true 141 | resolve() 142 | } 143 | script.onerror = function () { 144 | f6.scriptLoaded[url] = false 145 | reject(new Error('Could not load script at ' + url)); 146 | } 147 | script.src = url 148 | f6.one('head').appendChild(script) 149 | }) 150 | } 151 | 152 | /** ajax with 4 contentType , ref : https://imququ.com/post/four-ways-to-post-data-in-http.html 153 | * 1. application/x-www-form-urlencoded ex: title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3 154 | * 2. multipart/form-data ex: -...Content-Disposition: form-data; name="file"; filename="chrome.png" ... Content-Type: image/png 155 | * 3. application/json ex: JSON.stringify(o) 156 | * 4. text/plain ex: hello ! 157 | * 5. text/xml ex: ... 158 | * For form, use xhr.send(new window.FormData(form)) 159 | */ 160 | f6.ajax = function (arg) { 161 | var promise = new Promise(function (resolve, reject) { 162 | var xhr = new window.XMLHttpRequest() 163 | xhr.open(arg.method, arg.url, true) 164 | if (arg.contentType) { 165 | xhr.setRequestHeader('Content-Type', arg.contentType) 166 | } 167 | xhr.onreadystatechange = function () { 168 | if (xhr.readyState !== 4) return 169 | if (xhr.status === 200) { 170 | if (arg.alert) alert('Success!') 171 | resolve(xhr.responseText) 172 | } else { 173 | if (arg.alert) alert('Fail!') 174 | reject(new Error(xhr.statusText)) 175 | } 176 | } 177 | console.log('ajax:arg='+JSON.stringify(arg)) 178 | xhr.send(arg.body) 179 | }) 180 | return promise 181 | } 182 | 183 | f6.ojax = async function (arg, obj) { 184 | arg.contentType = 'application/json' 185 | if (obj) arg.body = JSON.stringify(obj) 186 | var json = await f6.ajax(arg) 187 | return JSON.parse(json) 188 | } 189 | 190 | f6.fjax = function (arg, form) { 191 | form.action = arg.url 192 | form.method = arg.method 193 | // arg.contentType = 'multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA' 194 | arg.body = new window.FormData(form) 195 | return f6.ajax(arg) 196 | } 197 | 198 | f6.onload = function (init) { 199 | return new Promise(function (resolve, reject) { 200 | window.addEventListener('load', function () { 201 | init() 202 | window.onhashchange() 203 | resolve() 204 | }) 205 | }) 206 | } 207 | 208 | 209 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "f6", 3 | "version": "1.2.0", 4 | "description": "ES6 Front-End Framework", 5 | "main": "f6.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "async-busboy": "^0.4.0", 9 | "koa": "^2.2.0", 10 | "koa-bodyparser": "^4.2.0", 11 | "koa-logger": "^2.0.1", 12 | "koa-router": "^7.1.1", 13 | "koa-static": "^3.0.0", 14 | "mocha": "^3.1.0" 15 | }, 16 | "scripts": { 17 | "build": "browserify f6.js -o web/f6.js", 18 | "start": "browserify f6.js -o web/f6.js & node server", 19 | "test": "mocha" 20 | }, 21 | "bin": {}, 22 | "keywords": [ 23 | "SPA", 24 | "Framework", 25 | "AJAX", 26 | "ES6" 27 | ], 28 | "homepage": "https://github.com/ccckmit/f6", 29 | "repository": { 30 | "type": "git", 31 | "url": "http://github.com/ccckmit/f6.git" 32 | }, 33 | "author": { 34 | "name": "Chung-Chen Chen", 35 | "email": "ccckmit@gmail.com", 36 | "url": "http://mdbookspace.com/view/ccc/" 37 | }, 38 | "license": "MIT" 39 | } 40 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const koaStatic = require('koa-static') 3 | const app = new Koa() 4 | 5 | app.use(koaStatic('web')).listen(3000) 6 | -------------------------------------------------------------------------------- /web/ex1-dom/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 16 | 17 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /web/ex1-dom/plugin.html: -------------------------------------------------------------------------------- 1 |
Hi!
2 |
Hi!
3 |
Hi!
4 |
Hi!
5 |
Hi!
6 |
Hi!
7 |
Hi!
8 | 9 | -------------------------------------------------------------------------------- /web/ex1-dom/plugin.js: -------------------------------------------------------------------------------- 1 | var i = 1 2 | // f6.all('#pluginBox div').each((x)=>x.innerHTML = `i=${i++}`) 3 | f6.all('#pluginBox div').each(x => x.html(`i=${i++}`)) 4 | 5 | f6.on('#hideOdd', 'click', function () { 6 | // f6.all('#pluginBox div:nth-child(odd)').each(f6.hide) 7 | f6.all('#pluginBox div:nth-child(odd)').hide() 8 | }) 9 | -------------------------------------------------------------------------------- /web/ex2-router/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
#hello
6 |
#list/3373
7 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /web/ex3-blogInClient/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 55 | 56 | 57 |
58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /web/ex3-blogInClient/main.js: -------------------------------------------------------------------------------- 1 | var content, title 2 | var posts = [] 3 | 4 | async function main () { 5 | f6.route(/^$/, list) 6 | .route(/^post\/new/, add) 7 | .route(/^post\/(\w+?)/, show) 8 | .route(/^post/, create) 9 | await f6.onload(init) 10 | } 11 | 12 | function init() { 13 | title = f6.one('title') 14 | content = f6.one('#content') 15 | } 16 | 17 | async function add () { 18 | title.innerHTML = 'New Post' 19 | content.innerHTML = ` 20 |

New Post

21 |

Create a new post.

22 |
23 |

24 |

25 |

26 |
27 | ` 28 | } 29 | 30 | async function create () { 31 | var post = { 32 | id: posts.length, 33 | title: f6.one('#addTitle').value, 34 | body: f6.one('#addBody').value, 35 | created_at: new Date(), 36 | } 37 | posts.push(post) 38 | // alert('before go()') 39 | await f6.go('') // list # 40 | // alert('after go()') 41 | } 42 | 43 | async function show (m) { 44 | var id = parseInt(m[1]) 45 | var post = posts[id] 46 | content.innerHTML = ` 47 |

${post.title}

48 |

${post.body}

49 | ` 50 | } 51 | 52 | async function list () { 53 | title.innerHTML = 'Posts' 54 | content.innerHTML = 55 | `

Posts

56 |

You have ${posts.length} posts!

57 |

Create a Post

58 | 66 | ` 67 | } 68 | 69 | main() 70 | -------------------------------------------------------------------------------- /web/ex4-blogInServer/server.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa') 2 | var Router = require('koa-router') 3 | var logger = require('koa-logger') 4 | var bodyParser = require('koa-bodyparser') 5 | 6 | var app = new Koa() 7 | var router = new Router() 8 | var posts = [] 9 | 10 | app.use(bodyParser()) 11 | app.use(logger()) 12 | 13 | router 14 | .get('/', list) 15 | .get('/post/new', add) 16 | .get('/post/:id', show) 17 | .post('/post', create) 18 | 19 | async function add (ctx) { 20 | ctx.body = newRender() 21 | } 22 | async function show (ctx) { 23 | var post = posts[ctx.params.id] 24 | if (!post) ctx.throw(404, 'invalid post id') 25 | ctx.body = await showRender(post) 26 | } 27 | 28 | async function create (ctx) { 29 | var post = ctx.request.body 30 | var id = posts.push(post) - 1 31 | post.created_at = new Date() 32 | post.id = id 33 | ctx.redirect('/') 34 | } 35 | 36 | async function list (ctx) { 37 | ctx.body = listRender(posts) 38 | } 39 | 40 | function layoutRender (title, content) { 41 | return ` 42 | 43 | 44 | ${title} 45 | 95 | 96 | 97 |
98 | ${content} 99 |
100 | 101 | 102 | ` 103 | } 104 | 105 | function newRender () { 106 | return layoutRender('New Post', ` 107 |

New Post

108 |

Create a new post.

109 |
110 |

111 |

25 |

26 |
27 | ` 28 | } 29 | 30 | async function create () { 31 | var post = { 32 | title: f6.one('#addTitle').value, 33 | body: f6.one('#addBody').value, 34 | created_at: new Date() 35 | } 36 | console.log(`create:post=${JSON.stringify(post)}`) 37 | await f6.ojax({method: 'POST', url: '/post'}, post) 38 | // posts.push(post) 39 | f6.go('') // list # 40 | } 41 | 42 | async function show (m) { 43 | var id = parseInt(m[1]) 44 | var post = await f6.ojax({method: 'GET', url: `/post/${id}`}) 45 | // var post = posts[id] 46 | content.innerHTML = ` 47 |

${post.title}

48 |

${post.body}

49 | ` 50 | } 51 | 52 | async function list () { 53 | var posts = await f6.ojax({method: 'GET', url: '/'}) 54 | title.innerHTML = 'Posts' 55 | content.innerHTML = 56 | `

Posts

57 |

You have ${posts.length} posts!

58 |

Create a Post

59 | 67 | ` 68 | } 69 | 70 | main() 71 | -------------------------------------------------------------------------------- /web/ex5-blogInAjax/server.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa') 2 | var Router = require('koa-router') 3 | var logger = require('koa-logger') 4 | var koaStatic = require('koa-static') 5 | var bodyParser = require('koa-bodyparser') 6 | 7 | var app = new Koa() 8 | var router = new Router() 9 | 10 | app.use(bodyParser()) 11 | 12 | var posts = [] 13 | 14 | app.use(logger()) 15 | 16 | router 17 | .get('/', listPosts) 18 | .get('/post/:id', getPost) 19 | .post('/post', createPost) 20 | 21 | async function listPosts (ctx) { 22 | ctx.body = JSON.stringify(posts) 23 | } 24 | 25 | async function getPost (ctx) { 26 | var id = parseInt(ctx.params.id) 27 | console.log('getpost: id=%d posts=%j', id, posts) 28 | var post = posts[id] 29 | if (!post) ctx.throw(404, 'invalid post id') 30 | ctx.body = await JSON.stringify(post) 31 | } 32 | 33 | async function createPost (ctx) { 34 | console.log('createPost:rawBody=%s', ctx.request.rawBody) 35 | console.log('createPost:body=%j', ctx.request.body) 36 | var post = ctx.request.body 37 | var id = posts.push(post) - 1 38 | post.created_at = new Date() 39 | post.id = id 40 | ctx.body = JSON.stringify(post) 41 | } 42 | 43 | app.use(router.routes()).use(koaStatic('../')).listen(3000) 44 | -------------------------------------------------------------------------------- /web/ex6-upload/README.md: -------------------------------------------------------------------------------- 1 | # 執行方法 2 | 3 | ``` 4 | $ node server 5 | ``` 6 | 7 | 然後啟動瀏覽器看 http://localhost:3000/upload.html 8 | 9 | 接著選擇幾個檔案,按下上傳! 10 | 11 | 您會看到這些檔案出現在 upload 資料夾當中。 12 | -------------------------------------------------------------------------------- /web/ex6-upload/server.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const serve = require('koa-static') 3 | const Router = require('koa-router') 4 | const fs = require('mz/fs') 5 | const asyncBusboy = require('async-busboy') 6 | 7 | const app = new Koa() 8 | const router = new Router() 9 | 10 | router.post('/upload', async function (ctx, next) { 11 | const {files, fields} = await asyncBusboy(ctx.req) 12 | console.log('files=%s', JSON.stringify(files, null, 2)) 13 | console.log('fields=%j', fields) 14 | for (var i in files) { 15 | var file = files[i].filename 16 | console.log('file=%s', file) 17 | var stream = fs.createWriteStream('upload/' + file) 18 | files[i].pipe(stream) 19 | } 20 | ctx.body = JSON.stringify(files, null, 2) 21 | }) 22 | 23 | app.use(serve('../')).use(router.routes()).listen(3000) 24 | -------------------------------------------------------------------------------- /web/ex6-upload/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Upload 5 | 11 | 12 | 13 |

File Upload

14 |

Try uploading multiple files at a time.

15 |
16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /web/ex6-upload/upload/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc-js/f6/c9aeb868012155020f9ab6e20a0243aef1a02564/web/ex6-upload/upload/empty.txt -------------------------------------------------------------------------------- /web/ex7-uploadAjax/README.md: -------------------------------------------------------------------------------- 1 | # 執行方法 2 | 3 | ``` 4 | $ node upload 5 | ``` 6 | 7 | 然後啟動瀏覽器看 http://localhost:3000/upload.html 8 | 9 | 接著選擇幾個檔案,按下上傳! 10 | 11 | 您會看到這些檔案出現在 upload 資料夾當中。 12 | -------------------------------------------------------------------------------- /web/ex7-uploadAjax/server.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const serve = require('koa-static') 3 | const Router = require('koa-router') 4 | const fs = require('mz/fs') 5 | const asyncBusboy = require('async-busboy') 6 | 7 | const app = new Koa() 8 | const router = new Router() 9 | 10 | router.post('/upload', async function (ctx, next) { 11 | const {files, fields} = await asyncBusboy(ctx.req) 12 | console.log('files=%s', JSON.stringify(files, null, 2)) 13 | console.log('fields=%j', fields) 14 | for (var i in files) { 15 | var file = files[i].filename 16 | console.log('file=%s', file) 17 | var stream = fs.createWriteStream('upload/' + file) 18 | files[i].pipe(stream) 19 | } 20 | ctx.body = JSON.stringify(files, null, 2) 21 | }) 22 | 23 | app.use(serve('../')).use(router.routes()).listen(3000) 24 | -------------------------------------------------------------------------------- /web/ex7-uploadAjax/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Upload 5 | 11 | 12 | 13 |

File Upload

14 |

Try uploading multiple files at a time.

15 |
16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /web/ex7-uploadAjax/upload/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc-js/f6/c9aeb868012155020f9ab6e20a0243aef1a02564/web/ex7-uploadAjax/upload/empty.txt -------------------------------------------------------------------------------- /web/ex8-blogInAjaxMongodb/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 55 | 56 | 57 |
58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /web/ex8-blogInAjaxMongodb/main.js: -------------------------------------------------------------------------------- 1 | var content, title 2 | 3 | async function main () { 4 | f6.route(/^$/, list) 5 | .route(/^post\/new/, add) 6 | .route(/^post\/(\w+)/, show) 7 | .route(/^post/, create) 8 | await f6.onload(init) 9 | } 10 | 11 | function init() { 12 | title = f6.one('title') 13 | content = f6.one('#content') 14 | } 15 | 16 | async function add () { 17 | title.innerHTML = 'New Post' 18 | content.innerHTML = ` 19 |

New Post

20 |

Create a new post.

21 |
22 |

23 |

24 |

25 |
26 | ` 27 | } 28 | 29 | async function create () { 30 | var post = { 31 | title: f6.one('#addTitle').value, 32 | body: f6.one('#addBody').value, 33 | created_at: new Date() 34 | } 35 | console.log(`create:post=${JSON.stringify(post)}`) 36 | await f6.ojax({method: 'POST', url: '/post'}, post) 37 | f6.go('') // list # 38 | } 39 | 40 | async function show (m) { 41 | var id = m[1] 42 | var post = await f6.ojax({method: 'GET', url: `/post/${id}`}) 43 | content.innerHTML = ` 44 |

${post.title}

45 |

${post.body}

46 | ` 47 | } 48 | 49 | async function list () { 50 | var posts = await f6.ojax({method: 'GET', url: '/'}) 51 | title.innerHTML = 'Posts' 52 | content.innerHTML = 53 | `

Posts

54 |

You have ${posts.length} posts!

55 |

Create a Post

56 | 64 | ` 65 | } 66 | 67 | main() 68 | -------------------------------------------------------------------------------- /web/ex8-blogInAjaxMongodb/server.js: -------------------------------------------------------------------------------- 1 | var Koa = require('koa') 2 | var Router = require('koa-router') 3 | var logger = require('koa-logger') 4 | var koaStatic = require('koa-static') 5 | var bodyParser = require('koa-bodyparser') 6 | var mongodb = require('mongodb') 7 | 8 | var db, postsTable 9 | 10 | var app = new Koa() 11 | var router = new Router() 12 | 13 | app.use(bodyParser()) 14 | app.use(logger()) 15 | 16 | router 17 | .get('/', listPosts) 18 | .get('/post/:id', getPost) 19 | .post('/post', createPost) 20 | 21 | async function listPosts (ctx) { 22 | var posts = await postsTable.find({}).toArray() 23 | console.log('list:posts = %j', posts) 24 | ctx.body = JSON.stringify(posts) 25 | } 26 | 27 | async function getPost (ctx) { 28 | try { 29 | var id = ctx.params.id 30 | var post = await postsTable.findOne({_id: new mongodb.ObjectID(id)}) 31 | if (!post) ctx.throw(404, 'invalid post id') 32 | ctx.body = await JSON.stringify(post) 33 | } catch (error) { 34 | ctx.throw(500) 35 | } 36 | } 37 | 38 | async function createPost (ctx) { 39 | try { 40 | console.log('createPost:rawBody=%s', ctx.request.rawBody) 41 | console.log('createPost:body=%j', ctx.request.body) 42 | var post = ctx.request.body 43 | post.created_at = new Date() 44 | await postsTable.insert(post) 45 | ctx.body = JSON.stringify(post) 46 | } catch (error) { 47 | ctx.throw(500) 48 | ctx.body = {} 49 | } 50 | } 51 | 52 | async function main () { 53 | db = await mongodb.MongoClient.connect('mongodb://127.0.0.1:27017/blog') 54 | postsTable = db.collection('posts') 55 | app.use(router.routes()).use(koaStatic('../')).listen(3000) 56 | } 57 | 58 | main() 59 | -------------------------------------------------------------------------------- /web/f6.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o route 8 | f6.route = function (regexp, f) { 9 | f6.router.map.set(regexp, f) 10 | return this 11 | } 12 | 13 | f6.onhash = function () { 14 | var promise = new Promise(function (resolve, reject) { 15 | var hash = window.location.hash.trim().substring(1) 16 | var m 17 | for (let [regexp, f] of f6.router.map) { 18 | m = hash.match(regexp) 19 | if (m) { 20 | f(m, hash) 21 | resolve(m) 22 | break 23 | } 24 | } 25 | if (!m) reject(new Error('no route match hash')) 26 | }) 27 | return promise 28 | } 29 | 30 | window.onhashchange = function () { 31 | f6.onhash() 32 | } 33 | 34 | f6.go = function (hash) { 35 | window.location.hash = '#' + hash 36 | return f6.onhash() 37 | // return this 38 | } 39 | 40 | // DOM Element 41 | Element.prototype.one = function (selector) { 42 | return this.querySelector(selector) 43 | } 44 | 45 | Element.prototype.all = function (selector) { 46 | return this.querySelectorAll(selector) 47 | } 48 | 49 | Element.prototype.hide = function () { 50 | this.hidden = true 51 | return this 52 | } 53 | 54 | Element.prototype.show = function () { 55 | this.hidden = undefined 56 | return this 57 | } 58 | 59 | Element.prototype.html = function (html) { 60 | this.innerHTML = html 61 | return this 62 | } 63 | 64 | // NodeList 65 | NodeList.prototype.each = function (f) { 66 | this.forEach(f) 67 | return this 68 | } 69 | 70 | NodeList.prototype.hide = function () { 71 | this.each(function (x) { x.hide() }) 72 | return this 73 | } 74 | 75 | NodeList.prototype.show = function () { 76 | this.each(function (x) { x.show() }) 77 | return this 78 | } 79 | 80 | NodeList.prototype.html = function (html) { 81 | this.each(function (x) { x.html(html) }) 82 | return this 83 | } 84 | 85 | // ==> not test yet 86 | f6.nodes = function (html) { 87 | var div = document.createElement('div') 88 | div.innerHTML = html 89 | var childNodes = div.childNodes 90 | delete div 91 | return childNodes 92 | } 93 | 94 | NodeList.prototype.unshift = function (html) { 95 | var nodes = f6.nodes(html) 96 | for (var i in div.childNodes) { 97 | this.insertBefore(nodes[i], this.firstChild) 98 | } 99 | return this 100 | } 101 | 102 | NodeList.prototype.push = function (html) { 103 | var nodes = f6.nodes(html) 104 | for (var i in div.childNodes) { 105 | this.appendChild(nodes[i]) 106 | } 107 | return this 108 | } 109 | 110 | // DOM short cut 111 | f6.one = function (selector) { 112 | return document.querySelector(selector) 113 | } 114 | 115 | f6.all = function (selector) { 116 | return document.querySelectorAll(selector) 117 | } 118 | 119 | // View : Event Handling 120 | f6.on = function (obj, event, f) { 121 | var o = (typeof obj === 'string') ? f6.one(obj) : obj 122 | o.addEventListener(event, f) 123 | } 124 | 125 | // load stylesheet (CSS) 126 | f6.styleLoad = function (url) { 127 | var ss = document.createElement('link') 128 | ss.type = 'text/css' 129 | ss.rel = 'stylesheet' 130 | ss.href = url 131 | f6.one('head').appendChild(ss) 132 | } 133 | 134 | // load script (JS) 135 | f6.scriptLoad = function (url) { 136 | return new Promise(function (resolve, reject) { 137 | var urlLoaded = f6.scriptLoaded[url] 138 | if (urlLoaded === true) resolve(url) 139 | var script = document.createElement('script') 140 | script.onload = function () { 141 | f6.scriptLoaded[url] = true 142 | resolve() 143 | } 144 | script.onerror = function () { 145 | f6.scriptLoaded[url] = false 146 | reject(new Error('Could not load script at ' + url)); 147 | } 148 | script.src = url 149 | f6.one('head').appendChild(script) 150 | }) 151 | } 152 | 153 | /** ajax with 4 contentType , ref : https://imququ.com/post/four-ways-to-post-data-in-http.html 154 | * 1. application/x-www-form-urlencoded ex: title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3 155 | * 2. multipart/form-data ex: -...Content-Disposition: form-data; name="file"; filename="chrome.png" ... Content-Type: image/png 156 | * 3. application/json ex: JSON.stringify(o) 157 | * 4. text/plain ex: hello ! 158 | * 5. text/xml ex: ... 159 | * For form, use xhr.send(new window.FormData(form)) 160 | */ 161 | f6.ajax = function (arg) { 162 | var promise = new Promise(function (resolve, reject) { 163 | var xhr = new window.XMLHttpRequest() 164 | xhr.open(arg.method, arg.url, true) 165 | if (arg.contentType) { 166 | xhr.setRequestHeader('Content-Type', arg.contentType) 167 | } 168 | xhr.onreadystatechange = function () { 169 | if (xhr.readyState !== 4) return 170 | if (xhr.status === 200) { 171 | if (arg.alert) alert('Success!') 172 | resolve(xhr.responseText) 173 | } else { 174 | if (arg.alert) alert('Fail!') 175 | reject(new Error(xhr.statusText)) 176 | } 177 | } 178 | console.log('ajax:arg='+JSON.stringify(arg)) 179 | xhr.send(arg.body) 180 | }) 181 | return promise 182 | } 183 | 184 | f6.ojax = async function (arg, obj) { 185 | arg.contentType = 'application/json' 186 | if (obj) arg.body = JSON.stringify(obj) 187 | var json = await f6.ajax(arg) 188 | return JSON.parse(json) 189 | } 190 | 191 | f6.fjax = function (arg, form) { 192 | form.action = arg.url 193 | form.method = arg.method 194 | // arg.contentType = 'multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA' 195 | arg.body = new window.FormData(form) 196 | return f6.ajax(arg) 197 | } 198 | 199 | f6.onload = function (init) { 200 | return new Promise(function (resolve, reject) { 201 | window.addEventListener('load', function () { 202 | init() 203 | window.onhashchange() 204 | resolve() 205 | }) 206 | }) 207 | } 208 | 209 | 210 | 211 | },{}]},{},[1]); 212 | --------------------------------------------------------------------------------