├── static ├── .gitkeep └── js │ └── qq-wechat-emotion-parser.js ├── src ├── data │ ├── chat.js │ ├── list.js │ └── banner.json ├── assets │ ├── head.jpeg │ ├── font-awesome │ │ └── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ ├── js │ │ ├── zepto │ │ │ ├── ie.js │ │ │ ├── stack.js │ │ │ ├── assets.js │ │ │ ├── gesture.js │ │ │ ├── ios3.js │ │ │ ├── form.js │ │ │ ├── fx_methods.js │ │ │ ├── data.js │ │ │ ├── selector.js │ │ │ ├── detect.js │ │ │ ├── deferred.js │ │ │ ├── callbacks.js │ │ │ ├── fx.js │ │ │ ├── touch.js │ │ │ ├── event.js │ │ │ └── ajax.js │ │ ├── common.js │ │ ├── common.js.map │ │ └── touch.js │ └── css │ │ └── normalize.css ├── components │ ├── Message.vue │ ├── BarItem.vue │ ├── SliderItem.vue │ ├── SliderArrows.vue │ ├── Bar.vue │ ├── SliderDots.vue │ ├── DraggableHeaderView.vue │ ├── MediaMangerItem.vue │ ├── NavItem.vue │ ├── UserInfo.vue │ ├── menu.vue │ ├── MediaItem.vue │ ├── Login.vue │ ├── ListItem.vue │ ├── Slider.vue │ ├── DraggableHeader.vue │ ├── VideoItem.vue │ ├── InputBox.vue │ └── MessageItem.vue ├── views │ ├── AlertDoc.vue │ ├── Home.vue │ ├── User.vue │ ├── ChatRoom.vue │ ├── Loading.vue │ ├── Login.vue │ ├── MediaManger.vue │ ├── Media.vue │ ├── List.vue │ └── Chat.vue ├── App.vue ├── util.js └── main.js ├── .eslintignore ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── .babelrc ├── test ├── unit │ ├── .eslintrc │ ├── specs │ │ └── Hello.spec.js │ ├── index.js │ └── karma.conf.js └── e2e │ ├── specs │ └── test.js │ ├── custom-assertions │ └── elementCount.js │ ├── nightwatch.conf.js │ └── runner.js ├── .gitignore ├── .editorconfig ├── server.js ├── .eslintrc.js ├── README.md ├── index.html └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/data/chat.js: -------------------------------------------------------------------------------- 1 | chat.js -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/head.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jocelynthink/vue-chat/master/src/assets/head.jpeg -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jocelynthink/vue-chat/master/src/assets/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/assets/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jocelynthink/vue-chat/master/src/assets/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/assets/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jocelynthink/vue-chat/master/src/assets/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/assets/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jocelynthink/vue-chat/master/src/assets/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/assets/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jocelynthink/vue-chat/master/src/assets/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /src/data/list.js: -------------------------------------------------------------------------------- 1 | { 2 | { 3 | "name": "", 4 | "src": "", 5 | "message": "" 6 | }, 7 | { 8 | "name": "", 9 | "src": "", 10 | "message": "" 11 | }, 12 | } -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | node_modules/ 4 | dist/ 5 | npm-debug.log 6 | selenium-debug.log 7 | test/unit/coverage 8 | test/e2e/reports 9 | 10 | node_modules 11 | npm-debug.log 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/components/Message.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/views/AlertDoc.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.get('/', function (req, res) { 5 | res.send('Hello World!'); 6 | }); 7 | 8 | app.listen(3000, function () { 9 | console.log('Example app listening on port 3000!'); 10 | }); -------------------------------------------------------------------------------- /src/data/banner.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 200, 3 | "message": "获取成功", 4 | "data": [ 5 | { 6 | "content": "../assert/images/1.jpg" 7 | }, 8 | { 9 | "content": "/images/1.jpg" 10 | }, 11 | { 12 | "content": "/images/1.jpg" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/unit/specs/Hello.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Hello from 'src/components/Hello' 3 | 4 | describe('Hello.vue', () => { 5 | it('should render correct contents', () => { 6 | const vm = new Vue({ 7 | template: '
', 8 | components: { Hello } 9 | }).$mount() 10 | expect(vm.$el.querySelector('.hello h1').textContent).to.contain('Hello World!') 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/components/BarItem.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | browser 7 | .url('http://localhost:8080') 8 | .waitForElementVisible('#app', 5000) 9 | .assert.elementPresent('.logo') 10 | .assert.containsText('h1', 'Hello World!') 11 | .assert.elementCount('p', 3) 12 | .end() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/SliderItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | -------------------------------------------------------------------------------- /src/components/SliderArrows.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | sourceType: 'module' 5 | }, 6 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 7 | extends: 'standard', 8 | // required to lint *.vue files 9 | plugins: [ 10 | 'html' 11 | ], 12 | // add your custom rules here 13 | 'rules': { 14 | // allow paren-less arrow functions 15 | 'arrow-parens': 0, 16 | // allow debugger during development 17 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-chat 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # run unit tests 18 | npm run unit 19 | 20 | # run e2e tests 21 | npm run e2e 22 | 23 | # run all tests 24 | npm test 25 | ``` 26 | 27 | For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 28 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // Polyfill fn.bind() for PhantomJS 2 | /* eslint-disable no-extend-native */ 3 | Function.prototype.bind = require('function-bind') 4 | 5 | // require all test files (files that ends with .spec.js) 6 | var testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | var srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /src/assets/js/zepto/ie.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function(){ 6 | // getComputedStyle shouldn't freak out when called 7 | // without a valid element as argument 8 | try { 9 | getComputedStyle(undefined) 10 | } catch(e) { 11 | var nativeGetComputedStyle = getComputedStyle 12 | window.getComputedStyle = function(element, pseudoElement){ 13 | try { 14 | return nativeGetComputedStyle(element, pseudoElement) 15 | } catch(e) { 16 | return null 17 | } 18 | } 19 | } 20 | })() 21 | -------------------------------------------------------------------------------- /src/assets/js/zepto/stack.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | $.fn.end = function(){ 7 | return this.prevObject || $() 8 | } 9 | 10 | $.fn.andSelf = function(){ 11 | return this.add(this.prevObject || $()) 12 | } 13 | 14 | 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){ 15 | var fn = $.fn[property] 16 | $.fn[property] = function(){ 17 | var ret = fn.apply(this, arguments) 18 | ret.prevObject = this 19 | return ret 20 | } 21 | }) 22 | })(Zepto) 23 | -------------------------------------------------------------------------------- /src/assets/js/zepto/assets.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var cache = [], timeout 7 | 8 | $.fn.remove = function(){ 9 | return this.each(function(){ 10 | if(this.parentNode){ 11 | if(this.tagName === 'IMG'){ 12 | cache.push(this) 13 | this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' 14 | if (timeout) clearTimeout(timeout) 15 | timeout = setTimeout(function(){ cache = [] }, 60000) 16 | } 17 | this.parentNode.removeChild(this) 18 | } 19 | }) 20 | } 21 | })(Zepto) 22 | -------------------------------------------------------------------------------- /src/components/Bar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | -------------------------------------------------------------------------------- /src/components/SliderDots.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 35 | -------------------------------------------------------------------------------- /src/components/DraggableHeaderView.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 聊天记录 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/js/common.js: -------------------------------------------------------------------------------- 1 | !function(e){function a(t){if(d[t])return d[t].exports;var c=d[t]={exports:{},id:t,loaded:!1};return e[t].call(c.exports,c,c.exports,a),c.loaded=!0,c.exports}var t=window.webpackJsonp;window.webpackJsonp=function(n,r){for(var f,o,l=0,p=[];l 2 | 12 | 13 | 14 | 15 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | var server = require('../../build/dev-server.js') 4 | 5 | // 2. run the nightwatch test suite against it 6 | // to run in additional browsers: 7 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 8 | // 2. add it to the --env flag below 9 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 10 | // For more information on Nightwatch's config file, see 11 | // http://nightwatchjs.org/guide#settings-file 12 | var opts = process.argv.slice(2) 13 | if (opts.indexOf('--config') === -1) { 14 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 15 | } 16 | if (opts.indexOf('--env') === -1) { 17 | opts = opts.concat(['--env', 'chrome']) 18 | } 19 | 20 | var spawn = require('cross-spawn') 21 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 22 | 23 | runner.on('exit', function (code) { 24 | server.close() 25 | process.exit(code) 26 | }) 27 | 28 | runner.on('error', function (err) { 29 | server.close() 30 | throw err 31 | }) 32 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 33 | 34 | 46 | -------------------------------------------------------------------------------- /src/components/NavItem.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | 43 | 57 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/htdocs/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 9000, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/js/zepto/gesture.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | if ($.os.ios) { 7 | var gesture = {}, gestureTimeout 8 | 9 | function parentIfText(node){ 10 | return 'tagName' in node ? node : node.parentNode 11 | } 12 | 13 | $(document).bind('gesturestart', function(e){ 14 | var now = Date.now(), delta = now - (gesture.last || now) 15 | gesture.target = parentIfText(e.target) 16 | gestureTimeout && clearTimeout(gestureTimeout) 17 | gesture.e1 = e.scale 18 | gesture.last = now 19 | }).bind('gesturechange', function(e){ 20 | gesture.e2 = e.scale 21 | }).bind('gestureend', function(e){ 22 | if (gesture.e2 > 0) { 23 | Math.abs(gesture.e1 - gesture.e2) != 0 && $(gesture.target).trigger('pinch') && 24 | $(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out')) 25 | gesture.e1 = gesture.e2 = gesture.last = 0 26 | } else if ('last' in gesture) { 27 | gesture = {} 28 | } 29 | }) 30 | 31 | ;['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){ 32 | $.fn[m] = function(callback){ return this.bind(m, callback) } 33 | }) 34 | } 35 | })(Zepto) 36 | -------------------------------------------------------------------------------- /src/assets/js/zepto/ios3.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function(undefined){ 6 | if (String.prototype.trim === undefined) // fix for iOS 3.2 7 | String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g, '') } 8 | 9 | // For iOS 3.x 10 | // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce 11 | if (Array.prototype.reduce === undefined) 12 | Array.prototype.reduce = function(fun){ 13 | if(this === void 0 || this === null) throw new TypeError() 14 | var t = Object(this), len = t.length >>> 0, k = 0, accumulator 15 | if(typeof fun != 'function') throw new TypeError() 16 | if(len == 0 && arguments.length == 1) throw new TypeError() 17 | 18 | if(arguments.length >= 2) 19 | accumulator = arguments[1] 20 | else 21 | do{ 22 | if(k in t){ 23 | accumulator = t[k++] 24 | break 25 | } 26 | if(++k >= len) throw new TypeError() 27 | } while (true) 28 | 29 | while (k < len){ 30 | if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t) 31 | k++ 32 | } 33 | return accumulator 34 | } 35 | 36 | })() 37 | -------------------------------------------------------------------------------- /src/assets/js/zepto/form.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | $.fn.serializeArray = function() { 7 | var name, type, result = [], 8 | add = function(value) { 9 | if (value.forEach) return value.forEach(add) 10 | result.push({ name: name, value: value }) 11 | } 12 | if (this[0]) $.each(this[0].elements, function(_, field){ 13 | type = field.type, name = field.name 14 | if (name && field.nodeName.toLowerCase() != 'fieldset' && 15 | !field.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' && 16 | ((type != 'radio' && type != 'checkbox') || field.checked)) 17 | add($(field).val()) 18 | }) 19 | return result 20 | } 21 | 22 | $.fn.serialize = function(){ 23 | var result = [] 24 | this.serializeArray().forEach(function(elm){ 25 | result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) 26 | }) 27 | return result.join('&') 28 | } 29 | 30 | $.fn.submit = function(callback) { 31 | if (0 in arguments) this.bind('submit', callback) 32 | else if (this.length) { 33 | var event = $.Event('submit') 34 | this.eq(0).trigger(event) 35 | if (!event.isDefaultPrevented()) this.get(0).submit() 36 | } 37 | return this 38 | } 39 | 40 | })(Zepto) 41 | -------------------------------------------------------------------------------- /src/components/UserInfo.vue: -------------------------------------------------------------------------------- 1 | 16 | 36 | 37 | 47 | -------------------------------------------------------------------------------- /src/views/User.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | 38 | 67 | -------------------------------------------------------------------------------- /src/components/menu.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 62 | -------------------------------------------------------------------------------- /src/views/ChatRoom.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 63 | 64 | 67 | -------------------------------------------------------------------------------- /src/views/Loading.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | 18 | 78 | -------------------------------------------------------------------------------- /src/components/MediaItem.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 64 | 65 | 85 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | import sha1 from 'sha1' 2 | import fetchJsonp from 'fetch-jsonp' 3 | 4 | const Util = { 5 | url: 'http://cs.nankebuluo.com/', 6 | //url: 'http://agent.nankebuluo.com/', 7 | cdnUrl: 'http://cdn.nankebuluo.com', 8 | imageUrl: 'http://cdn.nankebuluo.com/static/image', 9 | mediaUrl: 'http://cdn.nankebuluo.com/static/media', 10 | appid: 'wx8eec0e3d43f5362b', 11 | makeSignature(timestamp, nonce) { 12 | const token = 'swan' 13 | 14 | let arr = [token, timestamp, nonce].sort((a, b) => (a + '') > (b + '') ? 1 : -1) 15 | //alert(arr) 16 | return sha1(arr.join('')) 17 | }, 18 | 19 | getParam(name){ 20 | var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); 21 | var r = window.location.search.substr(1).match(reg); 22 | if (r != null) return unescape(r[2]); 23 | return null; 24 | }, 25 | 26 | fetchJsonp(url, data, options){ 27 | const timestamp = (+new Date() + '').slice(0, 10) 28 | const nonce = parseInt(Math.random() * 100) + '' 29 | const signature = this.makeSignature(timestamp, nonce) 30 | 31 | const _data = {...data, timestamp, nonce, signature} 32 | 33 | let _url = url 34 | let tem = [] 35 | for (let x in _data) { 36 | tem.push(x + '=' + _data[x]) 37 | } 38 | _url += '?' + tem.join('&') 39 | 40 | return fetchJsonp(_url, options) 41 | }, 42 | 43 | login(func){ 44 | const redirectUri = this.url + 'htdocs/index.html' 45 | const code = this.getParam('code') 46 | if (code) { 47 | const data = { 48 | code 49 | } 50 | fun.getJSON(this.url + 'Login.php?callback=?', data, json => { 51 | func(json); 52 | if (json.ret == 0) { 53 | } else { 54 | location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + this.appid + '&redirect_uri=' + encodeURIComponent(redirectUri) + '&response_type=code&scope=snsapi_userinfo&state=STAT' 55 | } 56 | }) 57 | } else { 58 | location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + this.appid + '&redirect_uri=' + encodeURIComponent(redirectUri) + '&response_type=code&scope=snsapi_base&state=STAT' 59 | } 60 | } 61 | } 62 | 63 | export default Util 64 | -------------------------------------------------------------------------------- /src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 35 | 36 | 78 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var path = require('path') 7 | var merge = require('webpack-merge') 8 | var baseConfig = require('../../build/webpack.base.conf') 9 | var utils = require('../../build/utils') 10 | var webpack = require('webpack') 11 | var projectRoot = path.resolve(__dirname, '../../') 12 | 13 | var webpackConfig = merge(baseConfig, { 14 | // use inline sourcemap for karma-sourcemap-loader 15 | module: { 16 | loaders: utils.styleLoaders() 17 | }, 18 | devtool: '#inline-source-map', 19 | vue: { 20 | loaders: { 21 | js: 'isparta' 22 | } 23 | }, 24 | plugins: [ 25 | new webpack.DefinePlugin({ 26 | 'process.env': require('../../config/test.env') 27 | }) 28 | ] 29 | }) 30 | 31 | // no need for app entry during tests 32 | delete webpackConfig.entry 33 | 34 | // make sure isparta loader is applied before eslint 35 | webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || [] 36 | webpackConfig.module.preLoaders.unshift({ 37 | test: /\.js$/, 38 | loader: 'isparta', 39 | include: path.resolve(projectRoot, 'src') 40 | }) 41 | 42 | // only apply babel for test files when using isparta 43 | webpackConfig.module.loaders.some(function (loader, i) { 44 | if (loader.loader === 'babel') { 45 | loader.include = path.resolve(projectRoot, 'test/unit') 46 | return true 47 | } 48 | }) 49 | 50 | module.exports = function (config) { 51 | config.set({ 52 | // to run in additional browsers: 53 | // 1. install corresponding karma launcher 54 | // http://karma-runner.github.io/0.13/config/browsers.html 55 | // 2. add it to the `browsers` array below. 56 | browsers: ['PhantomJS'], 57 | frameworks: ['mocha', 'sinon-chai'], 58 | reporters: ['spec', 'coverage'], 59 | files: ['./index.js'], 60 | preprocessors: { 61 | './index.js': ['webpack', 'sourcemap'] 62 | }, 63 | webpack: webpackConfig, 64 | webpackMiddleware: { 65 | noInfo: true 66 | }, 67 | coverageReporter: { 68 | dir: './coverage', 69 | reporters: [ 70 | { type: 'lcov', subdir: '.' }, 71 | { type: 'text-summary' } 72 | ] 73 | } 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/js/zepto/fx_methods.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($, undefined){ 6 | var document = window.document, docElem = document.documentElement, 7 | origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle 8 | 9 | function anim(el, speed, opacity, scale, callback) { 10 | if (typeof speed == 'function' && !callback) callback = speed, speed = undefined 11 | var props = { opacity: opacity } 12 | if (scale) { 13 | props.scale = scale 14 | el.css($.fx.cssPrefix + 'transform-origin', '0 0') 15 | } 16 | return el.animate(props, speed, null, callback) 17 | } 18 | 19 | function hide(el, speed, scale, callback) { 20 | return anim(el, speed, 0, scale, function(){ 21 | origHide.call($(this)) 22 | callback && callback.call(this) 23 | }) 24 | } 25 | 26 | $.fn.show = function(speed, callback) { 27 | origShow.call(this) 28 | if (speed === undefined) speed = 0 29 | else this.css('opacity', 0) 30 | return anim(this, speed, 1, '1,1', callback) 31 | } 32 | 33 | $.fn.hide = function(speed, callback) { 34 | if (speed === undefined) return origHide.call(this) 35 | else return hide(this, speed, '0,0', callback) 36 | } 37 | 38 | $.fn.toggle = function(speed, callback) { 39 | if (speed === undefined || typeof speed == 'boolean') 40 | return origToggle.call(this, speed) 41 | else return this.each(function(){ 42 | var el = $(this) 43 | el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback) 44 | }) 45 | } 46 | 47 | $.fn.fadeTo = function(speed, opacity, callback) { 48 | return anim(this, speed, opacity, null, callback) 49 | } 50 | 51 | $.fn.fadeIn = function(speed, callback) { 52 | var target = this.css('opacity') 53 | if (target > 0) this.css('opacity', 0) 54 | else target = 1 55 | return origShow.call(this).fadeTo(speed, target, callback) 56 | } 57 | 58 | $.fn.fadeOut = function(speed, callback) { 59 | return hide(this, speed, null, callback) 60 | } 61 | 62 | $.fn.fadeToggle = function(speed, callback) { 63 | return this.each(function(){ 64 | var el = $(this) 65 | el[ 66 | (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut' 67 | ](speed, callback) 68 | }) 69 | } 70 | 71 | })(Zepto) 72 | -------------------------------------------------------------------------------- /src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 48 | 49 | 91 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import Router from 'vue-router' 4 | import VueTouch from 'vue-touch' 5 | 6 | import 'whatwg-fetch' 7 | import 'babel-polyfill' 8 | 9 | import Util from './util' 10 | //import 'moment' 11 | 12 | //import './assets/js/qq-wechat-emotion-parser' 13 | //import Home from './views/Home' 14 | //import Tasks from './views/ChatRoom' 15 | import List from './views/List' 16 | import Chat from './views/Chat' 17 | //import Media from './views/Media' 18 | //import User from './views/User' 19 | //import MediaManger from './views/MediaManger' 20 | // import AlertDoc from './views/AlertDoc' 21 | //import DraggableHeader from './components/DraggableHeader' 22 | //import Loading from './views/Loading' 23 | // import Login from './views/Login' 24 | /* eslint-disable no-new */ 25 | // new Vue({ 26 | // el: 'body', 27 | // components: { App } 28 | // }) 29 | VueTouch.config.swipe = { 30 | direction: 'horizontal' 31 | }; 32 | 33 | Vue.use(Router) 34 | Vue.use(VueTouch) 35 | 36 | var router = new Router() 37 | Vue.config.debug = true; 38 | 39 | router.map({ 40 | '/': { 41 | component: List 42 | }, 43 | //'/home': { 44 | // component: Home 45 | //}, 46 | //'/tasks': { 47 | // component: Tasks 48 | //}, 49 | '/list': { 50 | component: List 51 | }, 52 | '/chat/:openid': { 53 | name: 'chat', 54 | component: Chat 55 | }, 56 | //'/mediamanger':{ 57 | // component: MediaManger 58 | //}, 59 | //'/media/:mediaid/:mediatype': { 60 | // name: 'media', 61 | // component: Media 62 | //}, 63 | //'/DraggableHeader' : { 64 | // component: DraggableHeader 65 | //}, 66 | //'/user' : { 67 | // component: User 68 | //} 69 | // }, 70 | // '/login' : { 71 | // component: Login 72 | // } 73 | }) 74 | 75 | router.beforeEach(({to, from, next}) => { 76 | let toPath = to.path 77 | let fromPath = from.path 78 | console.log('to: ' + toPath + ' from: ' + fromPath); 79 | if (toPath.replace(/[^/]/g, '').length > 1 || toPath === '/mediamanger') { 80 | router.app.isIndex = false 81 | } 82 | else { 83 | let depath = toPath === '/' 84 | router.app.isIndex = depath ? 0 : 1 85 | } 86 | 87 | next() 88 | }) 89 | 90 | router.afterEach(function ({to}) { 91 | console.log(`success to: ${to.path}`) 92 | }) 93 | 94 | //Util.login(function (json) { 95 | // if (json.ret == 0) { 96 | // router.start(App, '#app') 97 | // } else { 98 | // console.log('not login') 99 | // } 100 | //}) 101 | router.start(App, '#app') 102 | -------------------------------------------------------------------------------- /src/components/ListItem.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 75 | 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-chat", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "jocelyn", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "build": "node build/build.js", 10 | "unit": "karma start test/unit/karma.conf.js --single-run", 11 | "e2e": "node test/e2e/runner.js", 12 | "test": "npm run unit && npm run e2e", 13 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" 14 | }, 15 | "dependencies": { 16 | "babel-polyfill": "^6.13.0", 17 | "babel-runtime": "^6.0.0", 18 | "express": "^4.14.0", 19 | "fetch-ie8": "^1.4.3", 20 | "fetch-jsonp": "^1.0.1", 21 | "normalize.css": "^4.2.0", 22 | "vue": "^1.0.21", 23 | "vue-loadmore": "^0.2.4", 24 | "vue-router": "^0.7.13", 25 | "vue-touch": "^1.1.0", 26 | "whatwg-fetch": "^1.0.0" 27 | }, 28 | "devDependencies": { 29 | "babel-core": "^6.0.0", 30 | "babel-loader": "^6.0.0", 31 | "babel-plugin-transform-runtime": "^6.0.0", 32 | "babel-preset-es2015": "^6.0.0", 33 | "babel-preset-stage-2": "^6.0.0", 34 | "chai": "^3.5.0", 35 | "chromedriver": "^2.21.2", 36 | "connect-history-api-fallback": "^1.1.0", 37 | "cross-spawn": "^2.1.5", 38 | "css-loader": "^0.23.0", 39 | "eslint": "^2.10.2", 40 | "eslint-config-standard": "^5.1.0", 41 | "eslint-friendly-formatter": "^2.0.5", 42 | "eslint-loader": "^1.3.0", 43 | "eslint-plugin-html": "^1.3.0", 44 | "eslint-plugin-promise": "^1.0.8", 45 | "eslint-plugin-standard": "^1.3.2", 46 | "eventsource-polyfill": "^0.9.6", 47 | "exports-loader": "^0.6.3", 48 | "express": "^4.13.3", 49 | "extract-text-webpack-plugin": "^1.0.1", 50 | "file-loader": "^0.8.4", 51 | "function-bind": "^1.0.2", 52 | "html-webpack-plugin": "^2.8.1", 53 | "http-proxy-middleware": "^0.12.0", 54 | "imports-loader": "^0.6.5", 55 | "inject-loader": "^2.0.1", 56 | "isparta-loader": "^2.0.0", 57 | "json-loader": "^0.5.4", 58 | "karma": "^0.13.15", 59 | "karma-coverage": "^0.5.5", 60 | "karma-mocha": "^0.2.2", 61 | "karma-phantomjs-launcher": "^1.0.0", 62 | "karma-sinon-chai": "^1.2.0", 63 | "karma-sourcemap-loader": "^0.3.7", 64 | "karma-spec-reporter": "0.0.24", 65 | "karma-webpack": "^1.7.0", 66 | "lolex": "^1.4.0", 67 | "mocha": "^2.4.5", 68 | "nightwatch": "^0.8.18", 69 | "ora": "^0.2.0", 70 | "phantomjs-prebuilt": "^2.1.3", 71 | "selenium-server": "2.53.0", 72 | "shelljs": "^0.6.0", 73 | "sinon": "^1.17.3", 74 | "sinon-chai": "^2.8.0", 75 | "url-loader": "^0.5.7", 76 | "vue-hot-reload-api": "^1.2.0", 77 | "vue-html-loader": "^1.0.0", 78 | "vue-loader": "^8.3.0", 79 | "vue-style-loader": "^1.0.0", 80 | "webpack": "^1.12.2", 81 | "webpack-dev-middleware": "^1.4.0", 82 | "webpack-hot-middleware": "^2.6.0", 83 | "webpack-merge": "^0.8.3" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/assets/js/zepto/data.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | // The following code is heavily inspired by jQuery's $.fn.data() 6 | 7 | ;(function($){ 8 | var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, 9 | exp = $.expando = 'Zepto' + (+new Date()), emptyArray = [] 10 | 11 | // Get value from node: 12 | // 1. first try key as given, 13 | // 2. then try camelized key, 14 | // 3. fall back to reading "data-*" attribute. 15 | function getData(node, name) { 16 | var id = node[exp], store = id && data[id] 17 | if (name === undefined) return store || setData(node) 18 | else { 19 | if (store) { 20 | if (name in store) return store[name] 21 | var camelName = camelize(name) 22 | if (camelName in store) return store[camelName] 23 | } 24 | return dataAttr.call($(node), name) 25 | } 26 | } 27 | 28 | // Store value under camelized key on node 29 | function setData(node, name, value) { 30 | var id = node[exp] || (node[exp] = ++$.uuid), 31 | store = data[id] || (data[id] = attributeData(node)) 32 | if (name !== undefined) store[camelize(name)] = value 33 | return store 34 | } 35 | 36 | // Read all "data-*" attributes from a node 37 | function attributeData(node) { 38 | var store = {} 39 | $.each(node.attributes || emptyArray, function(i, attr){ 40 | if (attr.name.indexOf('data-') == 0) 41 | store[camelize(attr.name.replace('data-', ''))] = 42 | $.zepto.deserializeValue(attr.value) 43 | }) 44 | return store 45 | } 46 | 47 | $.fn.data = function(name, value) { 48 | return value === undefined ? 49 | // set multiple values via object 50 | $.isPlainObject(name) ? 51 | this.each(function(i, node){ 52 | $.each(name, function(key, value){ setData(node, key, value) }) 53 | }) : 54 | // get value from first element 55 | (0 in this ? getData(this[0], name) : undefined) : 56 | // set value on all elements 57 | this.each(function(){ setData(this, name, value) }) 58 | } 59 | 60 | $.data = function(elem, name, value) { 61 | return $(elem).data(name, value) 62 | } 63 | 64 | $.hasData = function(elem) { 65 | var id = elem[exp], store = id && data[id] 66 | return store ? !$.isEmptyObject(store) : false 67 | } 68 | 69 | $.fn.removeData = function(names) { 70 | if (typeof names == 'string') names = names.split(/\s+/) 71 | return this.each(function(){ 72 | var id = this[exp], store = id && data[id] 73 | if (store) $.each(names || store, function(key){ 74 | delete store[names ? camelize(this) : key] 75 | }) 76 | }) 77 | } 78 | 79 | // Generate extended `remove` and `empty` functions 80 | ;['remove', 'empty'].forEach(function(methodName){ 81 | var origFn = $.fn[methodName] 82 | $.fn[methodName] = function() { 83 | var elements = this.find('*') 84 | if (methodName === 'remove') elements = elements.add(this) 85 | elements.removeData() 86 | return origFn.call(this) 87 | } 88 | }) 89 | })(Zepto) 90 | -------------------------------------------------------------------------------- /src/assets/js/zepto/selector.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches 7 | 8 | function visible(elem){ 9 | elem = $(elem) 10 | return !!(elem.width() || elem.height()) && elem.css("display") !== "none" 11 | } 12 | 13 | // Implements a subset from: 14 | // http://api.jquery.com/category/selectors/jquery-selector-extensions/ 15 | // 16 | // Each filter function receives the current index, all nodes in the 17 | // considered set, and a value if there were parentheses. The value 18 | // of `this` is the node currently being considered. The function returns the 19 | // resulting node(s), null, or undefined. 20 | // 21 | // Complex selectors are not supported: 22 | // li:has(label:contains("foo")) + li:has(label:contains("bar")) 23 | // ul.inner:first > li 24 | var filters = $.expr[':'] = { 25 | visible: function(){ if (visible(this)) return this }, 26 | hidden: function(){ if (!visible(this)) return this }, 27 | selected: function(){ if (this.selected) return this }, 28 | checked: function(){ if (this.checked) return this }, 29 | parent: function(){ return this.parentNode }, 30 | first: function(idx){ if (idx === 0) return this }, 31 | last: function(idx, nodes){ if (idx === nodes.length - 1) return this }, 32 | eq: function(idx, _, value){ if (idx === value) return this }, 33 | contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this }, 34 | has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this } 35 | } 36 | 37 | var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'), 38 | childRe = /^\s*>/, 39 | classTag = 'Zepto' + (+new Date()) 40 | 41 | function process(sel, fn) { 42 | // quote the hash in `a[href^=#]` expression 43 | sel = sel.replace(/=#\]/g, '="#"]') 44 | var filter, arg, match = filterRe.exec(sel) 45 | if (match && match[2] in filters) { 46 | filter = filters[match[2]], arg = match[3] 47 | sel = match[1] 48 | if (arg) { 49 | var num = Number(arg) 50 | if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '') 51 | else arg = num 52 | } 53 | } 54 | return fn(sel, filter, arg) 55 | } 56 | 57 | zepto.qsa = function(node, selector) { 58 | return process(selector, function(sel, filter, arg){ 59 | try { 60 | var taggedParent 61 | if (!sel && filter) sel = '*' 62 | else if (childRe.test(sel)) 63 | // support "> *" child queries by tagging the parent node with a 64 | // unique class and prepending that classname onto the selector 65 | taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel 66 | 67 | var nodes = oldQsa(node, sel) 68 | } catch(e) { 69 | console.error('error performing selector: %o', selector) 70 | throw e 71 | } finally { 72 | if (taggedParent) taggedParent.removeClass(classTag) 73 | } 74 | return !filter ? nodes : 75 | zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) })) 76 | }) 77 | } 78 | 79 | zepto.matches = function(node, selector){ 80 | return process(selector, function(sel, filter, arg){ 81 | return (!sel || oldMatches(node, sel)) && 82 | (!filter || filter.call(node, null, arg) === node) 83 | }) 84 | } 85 | })(Zepto) 86 | -------------------------------------------------------------------------------- /src/components/Slider.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 92 | 93 | 160 | -------------------------------------------------------------------------------- /src/assets/js/common.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/bootstrap 6e0e0a4bd70a9133a00f"],"names":[],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAQ,oBAAoB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,YAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,gFAAwE,mPAAmP;AAC3T;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA","file":"common.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, callbacks = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId])\n \t\t\t\tcallbacks.push.apply(callbacks, installedChunks[chunkId]);\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);\n \t\twhile(callbacks.length)\n \t\t\tcallbacks.shift().call(null, __webpack_require__);\n \t\tif(moreModules[0]) {\n \t\t\tinstalledModules[0] = 0;\n \t\t\treturn __webpack_require__(0);\n \t\t}\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// \"0\" means \"already loaded\"\n \t// Array means \"loading\", array contains callbacks\n \tvar installedChunks = {\n \t\t9:0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId, callback) {\n \t\t// \"0\" is the signal for \"already loaded\"\n \t\tif(installedChunks[chunkId] === 0)\n \t\t\treturn callback.call(null, __webpack_require__);\n\n \t\t// an array means \"currently loading\".\n \t\tif(installedChunks[chunkId] !== undefined) {\n \t\t\tinstalledChunks[chunkId].push(callback);\n \t\t} else {\n \t\t\t// start chunk loading\n \t\t\tinstalledChunks[chunkId] = [callback];\n \t\t\tvar head = document.getElementsByTagName('head')[0];\n \t\t\tvar script = document.createElement('script');\n \t\t\tscript.type = 'text/javascript';\n \t\t\tscript.charset = 'utf-8';\n \t\t\tscript.async = true;\n\n \t\t\tscript.src = __webpack_require__.p + \"\" + chunkId + \".build.js?\" + {\"0\":\"b6ff1d52d65888b11b0d\",\"1\":\"3291d0d99fbddf12dbb8\",\"2\":\"34da392290c0ce34f3c9\",\"3\":\"92babfeac2e38a70e686\",\"4\":\"b24151215d1762d7bad8\",\"5\":\"082a831074b8501b437e\",\"6\":\"f72c281efb37dc2354ec\",\"7\":\"a90b46366ec70cc62096\",\"8\":\"e388421d8ea3638e6764\"}[chunkId] + \"\";\n \t\t\thead.appendChild(script);\n \t\t}\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/dist/\";\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 6e0e0a4bd70a9133a00f\n **/"],"sourceRoot":""} -------------------------------------------------------------------------------- /src/assets/js/zepto/detect.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | function detect(ua, platform){ 7 | var os = this.os = {}, browser = this.browser = {}, 8 | webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/), 9 | android = ua.match(/(Android);?[\s\/]+([\d.]+)?/), 10 | osx = !!ua.match(/\(Macintosh\; Intel /), 11 | ipad = ua.match(/(iPad).*OS\s([\d_]+)/), 12 | ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/), 13 | iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/), 14 | webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/), 15 | win = /Win\d{2}|Windows/.test(platform), 16 | wp = ua.match(/Windows Phone ([\d.]+)/), 17 | touchpad = webos && ua.match(/TouchPad/), 18 | kindle = ua.match(/Kindle\/([\d.]+)/), 19 | silk = ua.match(/Silk\/([\d._]+)/), 20 | blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/), 21 | bb10 = ua.match(/(BB10).*Version\/([\d.]+)/), 22 | rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/), 23 | playbook = ua.match(/PlayBook/), 24 | chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/), 25 | firefox = ua.match(/Firefox\/([\d.]+)/), 26 | firefoxos = ua.match(/\((?:Mobile|Tablet); rv:([\d.]+)\).*Firefox\/[\d.]+/), 27 | ie = ua.match(/MSIE\s([\d.]+)/) || ua.match(/Trident\/[\d](?=[^\?]+).*rv:([0-9.].)/), 28 | webview = !chrome && ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/), 29 | safari = webview || ua.match(/Version\/([\d.]+)([^S](Safari)|[^M]*(Mobile)[^S]*(Safari))/) 30 | 31 | // Todo: clean this up with a better OS/browser seperation: 32 | // - discern (more) between multiple browsers on android 33 | // - decide if kindle fire in silk mode is android or not 34 | // - Firefox on Android doesn't specify the Android version 35 | // - possibly devide in os, device and browser hashes 36 | 37 | if (browser.webkit = !!webkit) browser.version = webkit[1] 38 | 39 | if (android) os.android = true, os.version = android[2] 40 | if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.') 41 | if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.') 42 | if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null 43 | if (wp) os.wp = true, os.version = wp[1] 44 | if (webos) os.webos = true, os.version = webos[2] 45 | if (touchpad) os.touchpad = true 46 | if (blackberry) os.blackberry = true, os.version = blackberry[2] 47 | if (bb10) os.bb10 = true, os.version = bb10[2] 48 | if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2] 49 | if (playbook) browser.playbook = true 50 | if (kindle) os.kindle = true, os.version = kindle[1] 51 | if (silk) browser.silk = true, browser.version = silk[1] 52 | if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true 53 | if (chrome) browser.chrome = true, browser.version = chrome[1] 54 | if (firefox) browser.firefox = true, browser.version = firefox[1] 55 | if (firefoxos) os.firefoxos = true, os.version = firefoxos[1] 56 | if (ie) browser.ie = true, browser.version = ie[1] 57 | if (safari && (osx || os.ios || win)) { 58 | browser.safari = true 59 | if (!os.ios) browser.version = safari[1] 60 | } 61 | if (webview) browser.webview = true 62 | 63 | os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || 64 | (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/))) 65 | os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos || blackberry || bb10 || 66 | (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || 67 | (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/)))) 68 | } 69 | 70 | detect.call($, navigator.userAgent, navigator.platform) 71 | // make available to unit tests 72 | $.__detect = detect 73 | 74 | })(Zepto) 75 | -------------------------------------------------------------------------------- /src/views/MediaManger.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 65 | 66 | 153 | -------------------------------------------------------------------------------- /src/components/DraggableHeader.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 83 | 84 | 130 | -------------------------------------------------------------------------------- /src/views/Media.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 95 | 96 | 158 | 159 | -------------------------------------------------------------------------------- /src/assets/js/zepto/deferred.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | // 5 | // Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors 6 | 7 | ;(function($){ 8 | var slice = Array.prototype.slice 9 | 10 | function Deferred(func) { 11 | var tuples = [ 12 | // action, add listener, listener list, final state 13 | [ "resolve", "done", $.Callbacks({once:1, memory:1}), "resolved" ], 14 | [ "reject", "fail", $.Callbacks({once:1, memory:1}), "rejected" ], 15 | [ "notify", "progress", $.Callbacks({memory:1}) ] 16 | ], 17 | state = "pending", 18 | promise = { 19 | state: function() { 20 | return state 21 | }, 22 | always: function() { 23 | deferred.done(arguments).fail(arguments) 24 | return this 25 | }, 26 | then: function(/* fnDone [, fnFailed [, fnProgress]] */) { 27 | var fns = arguments 28 | return Deferred(function(defer){ 29 | $.each(tuples, function(i, tuple){ 30 | var fn = $.isFunction(fns[i]) && fns[i] 31 | deferred[tuple[1]](function(){ 32 | var returned = fn && fn.apply(this, arguments) 33 | if (returned && $.isFunction(returned.promise)) { 34 | returned.promise() 35 | .done(defer.resolve) 36 | .fail(defer.reject) 37 | .progress(defer.notify) 38 | } else { 39 | var context = this === promise ? defer.promise() : this, 40 | values = fn ? [returned] : arguments 41 | defer[tuple[0] + "With"](context, values) 42 | } 43 | }) 44 | }) 45 | fns = null 46 | }).promise() 47 | }, 48 | 49 | promise: function(obj) { 50 | return obj != null ? $.extend( obj, promise ) : promise 51 | } 52 | }, 53 | deferred = {} 54 | 55 | $.each(tuples, function(i, tuple){ 56 | var list = tuple[2], 57 | stateString = tuple[3] 58 | 59 | promise[tuple[1]] = list.add 60 | 61 | if (stateString) { 62 | list.add(function(){ 63 | state = stateString 64 | }, tuples[i^1][2].disable, tuples[2][2].lock) 65 | } 66 | 67 | deferred[tuple[0]] = function(){ 68 | deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments) 69 | return this 70 | } 71 | deferred[tuple[0] + "With"] = list.fireWith 72 | }) 73 | 74 | promise.promise(deferred) 75 | if (func) func.call(deferred, deferred) 76 | return deferred 77 | } 78 | 79 | $.when = function(sub) { 80 | var resolveValues = slice.call(arguments), 81 | len = resolveValues.length, 82 | i = 0, 83 | remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, 84 | deferred = remain === 1 ? sub : Deferred(), 85 | progressValues, progressContexts, resolveContexts, 86 | updateFn = function(i, ctx, val){ 87 | return function(value){ 88 | ctx[i] = this 89 | val[i] = arguments.length > 1 ? slice.call(arguments) : value 90 | if (val === progressValues) { 91 | deferred.notifyWith(ctx, val) 92 | } else if (!(--remain)) { 93 | deferred.resolveWith(ctx, val) 94 | } 95 | } 96 | } 97 | 98 | if (len > 1) { 99 | progressValues = new Array(len) 100 | progressContexts = new Array(len) 101 | resolveContexts = new Array(len) 102 | for ( ; i < len; ++i ) { 103 | if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) { 104 | resolveValues[i].promise() 105 | .done(updateFn(i, resolveContexts, resolveValues)) 106 | .fail(deferred.reject) 107 | .progress(updateFn(i, progressContexts, progressValues)) 108 | } else { 109 | --remain 110 | } 111 | } 112 | } 113 | if (!remain) deferred.resolveWith(resolveContexts, resolveValues) 114 | return deferred.promise() 115 | } 116 | 117 | $.Deferred = Deferred 118 | })(Zepto) 119 | -------------------------------------------------------------------------------- /src/components/VideoItem.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 123 | 124 | 172 | -------------------------------------------------------------------------------- /src/assets/js/zepto/callbacks.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | // Create a collection of callbacks to be fired in a sequence, with configurable behaviour 7 | // Option flags: 8 | // - once: Callbacks fired at most one time. 9 | // - memory: Remember the most recent context and arguments 10 | // - stopOnFalse: Cease iterating over callback list 11 | // - unique: Permit adding at most one instance of the same callback 12 | $.Callbacks = function(options) { 13 | options = $.extend({}, options) 14 | 15 | var memory, // Last fire value (for non-forgettable lists) 16 | fired, // Flag to know if list was already fired 17 | firing, // Flag to know if list is currently firing 18 | firingStart, // First callback to fire (used internally by add and fireWith) 19 | firingLength, // End of the loop when firing 20 | firingIndex, // Index of currently firing callback (modified by remove if needed) 21 | list = [], // Actual callback list 22 | stack = !options.once && [], // Stack of fire calls for repeatable lists 23 | fire = function(data) { 24 | memory = options.memory && data 25 | fired = true 26 | firingIndex = firingStart || 0 27 | firingStart = 0 28 | firingLength = list.length 29 | firing = true 30 | for ( ; list && firingIndex < firingLength ; ++firingIndex ) { 31 | if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) { 32 | memory = false 33 | break 34 | } 35 | } 36 | firing = false 37 | if (list) { 38 | if (stack) stack.length && fire(stack.shift()) 39 | else if (memory) list.length = 0 40 | else Callbacks.disable() 41 | } 42 | }, 43 | 44 | Callbacks = { 45 | add: function() { 46 | if (list) { 47 | var start = list.length, 48 | add = function(args) { 49 | $.each(args, function(_, arg){ 50 | if (typeof arg === "function") { 51 | if (!options.unique || !Callbacks.has(arg)) list.push(arg) 52 | } 53 | else if (arg && arg.length && typeof arg !== 'string') add(arg) 54 | }) 55 | } 56 | add(arguments) 57 | if (firing) firingLength = list.length 58 | else if (memory) { 59 | firingStart = start 60 | fire(memory) 61 | } 62 | } 63 | return this 64 | }, 65 | remove: function() { 66 | if (list) { 67 | $.each(arguments, function(_, arg){ 68 | var index 69 | while ((index = $.inArray(arg, list, index)) > -1) { 70 | list.splice(index, 1) 71 | // Handle firing indexes 72 | if (firing) { 73 | if (index <= firingLength) --firingLength 74 | if (index <= firingIndex) --firingIndex 75 | } 76 | } 77 | }) 78 | } 79 | return this 80 | }, 81 | has: function(fn) { 82 | return !!(list && (fn ? $.inArray(fn, list) > -1 : list.length)) 83 | }, 84 | empty: function() { 85 | firingLength = list.length = 0 86 | return this 87 | }, 88 | disable: function() { 89 | list = stack = memory = undefined 90 | return this 91 | }, 92 | disabled: function() { 93 | return !list 94 | }, 95 | lock: function() { 96 | stack = undefined 97 | if (!memory) Callbacks.disable() 98 | return this 99 | }, 100 | locked: function() { 101 | return !stack 102 | }, 103 | fireWith: function(context, args) { 104 | if (list && (!fired || stack)) { 105 | args = args || [] 106 | args = [context, args.slice ? args.slice() : args] 107 | if (firing) stack.push(args) 108 | else fire(args) 109 | } 110 | return this 111 | }, 112 | fire: function() { 113 | return Callbacks.fireWith(this, arguments) 114 | }, 115 | fired: function() { 116 | return !!fired 117 | } 118 | } 119 | 120 | return Callbacks 121 | } 122 | })(Zepto) 123 | -------------------------------------------------------------------------------- /src/components/InputBox.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 92 | 93 | 201 | -------------------------------------------------------------------------------- /src/assets/js/zepto/fx.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($, undefined){ 6 | var prefix = '', eventPrefix, 7 | vendors = { Webkit: 'webkit', Moz: '', O: 'o' }, 8 | testEl = document.createElement('div'), 9 | supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, 10 | transform, 11 | transitionProperty, transitionDuration, transitionTiming, transitionDelay, 12 | animationName, animationDuration, animationTiming, animationDelay, 13 | cssReset = {} 14 | 15 | function dasherize(str) { return str.replace(/([A-Z])/g, '-$1').toLowerCase() } 16 | function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() } 17 | 18 | if (testEl.style.transform === undefined) $.each(vendors, function(vendor, event){ 19 | if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { 20 | prefix = '-' + vendor.toLowerCase() + '-' 21 | eventPrefix = event 22 | return false 23 | } 24 | }) 25 | 26 | transform = prefix + 'transform' 27 | cssReset[transitionProperty = prefix + 'transition-property'] = 28 | cssReset[transitionDuration = prefix + 'transition-duration'] = 29 | cssReset[transitionDelay = prefix + 'transition-delay'] = 30 | cssReset[transitionTiming = prefix + 'transition-timing-function'] = 31 | cssReset[animationName = prefix + 'animation-name'] = 32 | cssReset[animationDuration = prefix + 'animation-duration'] = 33 | cssReset[animationDelay = prefix + 'animation-delay'] = 34 | cssReset[animationTiming = prefix + 'animation-timing-function'] = '' 35 | 36 | $.fx = { 37 | off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), 38 | speeds: { _default: 400, fast: 200, slow: 600 }, 39 | cssPrefix: prefix, 40 | transitionEnd: normalizeEvent('TransitionEnd'), 41 | animationEnd: normalizeEvent('AnimationEnd') 42 | } 43 | 44 | $.fn.animate = function(properties, duration, ease, callback, delay){ 45 | if ($.isFunction(duration)) 46 | callback = duration, ease = undefined, duration = undefined 47 | if ($.isFunction(ease)) 48 | callback = ease, ease = undefined 49 | if ($.isPlainObject(duration)) 50 | ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration 51 | if (duration) duration = (typeof duration == 'number' ? duration : 52 | ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 53 | if (delay) delay = parseFloat(delay) / 1000 54 | return this.anim(properties, duration, ease, callback, delay) 55 | } 56 | 57 | $.fn.anim = function(properties, duration, ease, callback, delay){ 58 | var key, cssValues = {}, cssProperties, transforms = '', 59 | that = this, wrappedCallback, endEvent = $.fx.transitionEnd, 60 | fired = false 61 | 62 | if (duration === undefined) duration = $.fx.speeds._default / 1000 63 | if (delay === undefined) delay = 0 64 | if ($.fx.off) duration = 0 65 | 66 | if (typeof properties == 'string') { 67 | // keyframe animation 68 | cssValues[animationName] = properties 69 | cssValues[animationDuration] = duration + 's' 70 | cssValues[animationDelay] = delay + 's' 71 | cssValues[animationTiming] = (ease || 'linear') 72 | endEvent = $.fx.animationEnd 73 | } else { 74 | cssProperties = [] 75 | // CSS transitions 76 | for (key in properties) 77 | if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') ' 78 | else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) 79 | 80 | if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) 81 | if (duration > 0 && typeof properties === 'object') { 82 | cssValues[transitionProperty] = cssProperties.join(', ') 83 | cssValues[transitionDuration] = duration + 's' 84 | cssValues[transitionDelay] = delay + 's' 85 | cssValues[transitionTiming] = (ease || 'linear') 86 | } 87 | } 88 | 89 | wrappedCallback = function(event){ 90 | if (typeof event !== 'undefined') { 91 | if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below" 92 | $(event.target).unbind(endEvent, wrappedCallback) 93 | } else 94 | $(this).unbind(endEvent, wrappedCallback) // triggered by setTimeout 95 | 96 | fired = true 97 | $(this).css(cssReset) 98 | callback && callback.call(this) 99 | } 100 | if (duration > 0){ 101 | this.bind(endEvent, wrappedCallback) 102 | // transitionEnd is not always firing on older Android phones 103 | // so make sure it gets fired 104 | setTimeout(function(){ 105 | if (fired) return 106 | wrappedCallback.call(that) 107 | }, ((duration + delay) * 1000) + 25) 108 | } 109 | 110 | // trigger page reflow so new elements can animate 111 | this.size() && this.get(0).clientLeft 112 | 113 | this.css(cssValues) 114 | 115 | if (duration <= 0) setTimeout(function() { 116 | that.each(function(){ wrappedCallback.call(this) }) 117 | }, 0) 118 | 119 | return this 120 | } 121 | 122 | testEl = null 123 | })(Zepto) 124 | -------------------------------------------------------------------------------- /src/assets/js/touch.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var touch = {}, 7 | touchTimeout, tapTimeout, swipeTimeout, longTapTimeout, 8 | longTapDelay = 750, 9 | gesture 10 | 11 | function swipeDirection(x1, x2, y1, y2) { 12 | return Math.abs(x1 - x2) >= 13 | Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') 14 | } 15 | 16 | function longTap() { 17 | longTapTimeout = null 18 | if (touch.last) { 19 | touch.el.trigger('longTap') 20 | touch = {} 21 | } 22 | } 23 | 24 | function cancelLongTap() { 25 | if (longTapTimeout) clearTimeout(longTapTimeout) 26 | longTapTimeout = null 27 | } 28 | 29 | function cancelAll() { 30 | if (touchTimeout) clearTimeout(touchTimeout) 31 | if (tapTimeout) clearTimeout(tapTimeout) 32 | if (swipeTimeout) clearTimeout(swipeTimeout) 33 | if (longTapTimeout) clearTimeout(longTapTimeout) 34 | touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null 35 | touch = {} 36 | } 37 | 38 | function isPrimaryTouch(event){ 39 | return (event.pointerType == 'touch' || 40 | event.pointerType == event.MSPOINTER_TYPE_TOUCH) 41 | && event.isPrimary 42 | } 43 | 44 | function isPointerEventType(e, type){ 45 | return (e.type == 'pointer'+type || 46 | e.type.toLowerCase() == 'mspointer'+type) 47 | } 48 | 49 | $(document).ready(function(){ 50 | var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType 51 | 52 | if ('MSGesture' in window) { 53 | gesture = new MSGesture() 54 | gesture.target = document.body 55 | } 56 | 57 | $(document) 58 | .bind('MSGestureEnd', function(e){ 59 | var swipeDirectionFromVelocity = 60 | e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null 61 | if (swipeDirectionFromVelocity) { 62 | touch.el.trigger('swipe') 63 | touch.el.trigger('swipe'+ swipeDirectionFromVelocity) 64 | } 65 | }) 66 | .on('touchstart MSPointerDown pointerdown', function(e){ 67 | if((_isPointerType = isPointerEventType(e, 'down')) && 68 | !isPrimaryTouch(e)) return 69 | firstTouch = _isPointerType ? e : e.touches[0] 70 | if (e.touches && e.touches.length === 1 && touch.x2) { 71 | // Clear out touch movement data if we have it sticking around 72 | // This can occur if touchcancel doesn't fire due to preventDefault, etc. 73 | touch.x2 = undefined 74 | touch.y2 = undefined 75 | } 76 | now = Date.now() 77 | delta = now - (touch.last || now) 78 | touch.el = $('tagName' in firstTouch.target ? 79 | firstTouch.target : firstTouch.target.parentNode) 80 | touchTimeout && clearTimeout(touchTimeout) 81 | touch.x1 = firstTouch.pageX 82 | touch.y1 = firstTouch.pageY 83 | if (delta > 0 && delta <= 250) touch.isDoubleTap = true 84 | touch.last = now 85 | longTapTimeout = setTimeout(longTap, longTapDelay) 86 | // adds the current touch contact for IE gesture recognition 87 | if (gesture && _isPointerType) gesture.addPointer(e.pointerId) 88 | }) 89 | .on('touchmove MSPointerMove pointermove', function(e){ 90 | if((_isPointerType = isPointerEventType(e, 'move')) && 91 | !isPrimaryTouch(e)) return 92 | firstTouch = _isPointerType ? e : e.touches[0] 93 | cancelLongTap() 94 | touch.x2 = firstTouch.pageX 95 | touch.y2 = firstTouch.pageY 96 | 97 | deltaX += Math.abs(touch.x1 - touch.x2) 98 | deltaY += Math.abs(touch.y1 - touch.y2) 99 | }) 100 | .on('touchend MSPointerUp pointerup', function(e){ 101 | if((_isPointerType = isPointerEventType(e, 'up')) && 102 | !isPrimaryTouch(e)) return 103 | cancelLongTap() 104 | 105 | // swipe 106 | if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || 107 | (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) 108 | 109 | swipeTimeout = setTimeout(function() { 110 | if (touch.el){ 111 | touch.el.trigger('swipe') 112 | touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) 113 | } 114 | touch = {} 115 | }, 0) 116 | 117 | // normal tap 118 | else if ('last' in touch) 119 | // don't fire tap when delta position changed by more than 30 pixels, 120 | // for instance when moving to a point and back to origin 121 | if (deltaX < 30 && deltaY < 30) { 122 | // delay by one tick so we can cancel the 'tap' event if 'scroll' fires 123 | // ('tap' fires before 'scroll') 124 | tapTimeout = setTimeout(function() { 125 | 126 | // trigger universal 'tap' with the option to cancelTouch() 127 | // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) 128 | var event = $.Event('tap') 129 | event.cancelTouch = cancelAll 130 | // [by paper] fix -> "TypeError: 'undefined' is not an object (evaluating 'touch.el.trigger'), when double tap 131 | if (touch.el) touch.el.trigger(event) 132 | 133 | // trigger double tap immediately 134 | if (touch.isDoubleTap) { 135 | if (touch.el) touch.el.trigger('doubleTap') 136 | touch = {} 137 | } 138 | 139 | // trigger single tap after 250ms of inactivity 140 | else { 141 | touchTimeout = setTimeout(function(){ 142 | touchTimeout = null 143 | if (touch.el) touch.el.trigger('singleTap') 144 | touch = {} 145 | }, 250) 146 | } 147 | }, 0) 148 | } else { 149 | touch = {} 150 | } 151 | deltaX = deltaY = 0 152 | 153 | }) 154 | // when the browser window loses focus, 155 | // for example when a modal dialog is shown, 156 | // cancel all ongoing events 157 | .on('touchcancel MSPointerCancel pointercancel', cancelAll) 158 | 159 | // scrolling the window indicates intention of the user 160 | // to scroll, not tap or swipe, so cancel all ongoing events 161 | $(window).on('scroll', cancelAll) 162 | }) 163 | 164 | ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 165 | 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(eventName){ 166 | $.fn[eventName] = function(callback){ return this.on(eventName, callback) } 167 | }) 168 | })(Zepto) 169 | -------------------------------------------------------------------------------- /src/assets/js/zepto/touch.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var touch = {}, 7 | touchTimeout, tapTimeout, swipeTimeout, longTapTimeout, 8 | longTapDelay = 750, 9 | gesture 10 | 11 | function swipeDirection(x1, x2, y1, y2) { 12 | return Math.abs(x1 - x2) >= 13 | Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') 14 | } 15 | 16 | function longTap() { 17 | longTapTimeout = null 18 | if (touch.last) { 19 | touch.el.trigger('longTap') 20 | touch = {} 21 | } 22 | } 23 | 24 | function cancelLongTap() { 25 | if (longTapTimeout) clearTimeout(longTapTimeout) 26 | longTapTimeout = null 27 | } 28 | 29 | function cancelAll() { 30 | if (touchTimeout) clearTimeout(touchTimeout) 31 | if (tapTimeout) clearTimeout(tapTimeout) 32 | if (swipeTimeout) clearTimeout(swipeTimeout) 33 | if (longTapTimeout) clearTimeout(longTapTimeout) 34 | touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null 35 | touch = {} 36 | } 37 | 38 | function isPrimaryTouch(event){ 39 | return (event.pointerType == 'touch' || 40 | event.pointerType == event.MSPOINTER_TYPE_TOUCH) 41 | && event.isPrimary 42 | } 43 | 44 | function isPointerEventType(e, type){ 45 | return (e.type == 'pointer'+type || 46 | e.type.toLowerCase() == 'mspointer'+type) 47 | } 48 | 49 | $(document).ready(function(){ 50 | var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType 51 | 52 | if ('MSGesture' in window) { 53 | gesture = new MSGesture() 54 | gesture.target = document.body 55 | } 56 | 57 | $(document) 58 | .bind('MSGestureEnd', function(e){ 59 | var swipeDirectionFromVelocity = 60 | e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null 61 | if (swipeDirectionFromVelocity) { 62 | touch.el.trigger('swipe') 63 | touch.el.trigger('swipe'+ swipeDirectionFromVelocity) 64 | } 65 | }) 66 | .on('touchstart MSPointerDown pointerdown', function(e){ 67 | if((_isPointerType = isPointerEventType(e, 'down')) && 68 | !isPrimaryTouch(e)) return 69 | firstTouch = _isPointerType ? e : e.touches[0] 70 | if (e.touches && e.touches.length === 1 && touch.x2) { 71 | // Clear out touch movement data if we have it sticking around 72 | // This can occur if touchcancel doesn't fire due to preventDefault, etc. 73 | touch.x2 = undefined 74 | touch.y2 = undefined 75 | } 76 | now = Date.now() 77 | delta = now - (touch.last || now) 78 | touch.el = $('tagName' in firstTouch.target ? 79 | firstTouch.target : firstTouch.target.parentNode) 80 | touchTimeout && clearTimeout(touchTimeout) 81 | touch.x1 = firstTouch.pageX 82 | touch.y1 = firstTouch.pageY 83 | if (delta > 0 && delta <= 250) touch.isDoubleTap = true 84 | touch.last = now 85 | longTapTimeout = setTimeout(longTap, longTapDelay) 86 | // adds the current touch contact for IE gesture recognition 87 | if (gesture && _isPointerType) gesture.addPointer(e.pointerId) 88 | }) 89 | .on('touchmove MSPointerMove pointermove', function(e){ 90 | if((_isPointerType = isPointerEventType(e, 'move')) && 91 | !isPrimaryTouch(e)) return 92 | firstTouch = _isPointerType ? e : e.touches[0] 93 | cancelLongTap() 94 | touch.x2 = firstTouch.pageX 95 | touch.y2 = firstTouch.pageY 96 | 97 | deltaX += Math.abs(touch.x1 - touch.x2) 98 | deltaY += Math.abs(touch.y1 - touch.y2) 99 | }) 100 | .on('touchend MSPointerUp pointerup', function(e){ 101 | if((_isPointerType = isPointerEventType(e, 'up')) && 102 | !isPrimaryTouch(e)) return 103 | cancelLongTap() 104 | 105 | // swipe 106 | if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || 107 | (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) 108 | 109 | swipeTimeout = setTimeout(function() { 110 | if (touch.el){ 111 | touch.el.trigger('swipe') 112 | touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) 113 | } 114 | touch = {} 115 | }, 0) 116 | 117 | // normal tap 118 | else if ('last' in touch) 119 | // don't fire tap when delta position changed by more than 30 pixels, 120 | // for instance when moving to a point and back to origin 121 | if (deltaX < 30 && deltaY < 30) { 122 | // delay by one tick so we can cancel the 'tap' event if 'scroll' fires 123 | // ('tap' fires before 'scroll') 124 | tapTimeout = setTimeout(function() { 125 | 126 | // trigger universal 'tap' with the option to cancelTouch() 127 | // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) 128 | var event = $.Event('tap') 129 | event.cancelTouch = cancelAll 130 | // [by paper] fix -> "TypeError: 'undefined' is not an object (evaluating 'touch.el.trigger'), when double tap 131 | if (touch.el) touch.el.trigger(event) 132 | 133 | // trigger double tap immediately 134 | if (touch.isDoubleTap) { 135 | if (touch.el) touch.el.trigger('doubleTap') 136 | touch = {} 137 | } 138 | 139 | // trigger single tap after 250ms of inactivity 140 | else { 141 | touchTimeout = setTimeout(function(){ 142 | touchTimeout = null 143 | if (touch.el) touch.el.trigger('singleTap') 144 | touch = {} 145 | }, 250) 146 | } 147 | }, 0) 148 | } else { 149 | touch = {} 150 | } 151 | deltaX = deltaY = 0 152 | 153 | }) 154 | // when the browser window loses focus, 155 | // for example when a modal dialog is shown, 156 | // cancel all ongoing events 157 | .on('touchcancel MSPointerCancel pointercancel', cancelAll) 158 | 159 | // scrolling the window indicates intention of the user 160 | // to scroll, not tap or swipe, so cancel all ongoing events 161 | $(window).on('scroll', cancelAll) 162 | }) 163 | 164 | ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 165 | 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(eventName){ 166 | $.fn[eventName] = function(callback){ return this.on(eventName, callback) } 167 | }) 168 | })(Zepto) 169 | -------------------------------------------------------------------------------- /src/views/List.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 140 | 141 | 282 | -------------------------------------------------------------------------------- /src/assets/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v4.2.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Change the default font family in all browsers (opinionated). 5 | * 2. Correct the line height in all browsers. 6 | * 3. Prevent adjustments of font size after orientation changes in IE and iOS. 7 | */ 8 | 9 | /* Document 10 | ========================================================================== */ 11 | 12 | html { 13 | font-family: sans-serif; /* 1 */ 14 | line-height: 1.15; /* 2 */ 15 | -ms-text-size-adjust: 100%; /* 3 */ 16 | -webkit-text-size-adjust: 100%; /* 3 */ 17 | } 18 | 19 | /* Sections 20 | ========================================================================== */ 21 | 22 | /** 23 | * Remove the margin in all browsers (opinionated). 24 | */ 25 | 26 | body { 27 | margin: 0; 28 | } 29 | 30 | /** 31 | * Add the correct display in IE 9-. 32 | */ 33 | 34 | article, 35 | aside, 36 | footer, 37 | header, 38 | nav, 39 | section { 40 | display: block; 41 | } 42 | 43 | /** 44 | * Correct the font size and margin on `h1` elements within `section` and 45 | * `article` contexts in Chrome, Firefox, and Safari. 46 | */ 47 | 48 | h1 { 49 | font-size: 2em; 50 | margin: 0.67em 0; 51 | } 52 | 53 | /* Grouping content 54 | ========================================================================== */ 55 | 56 | /** 57 | * Add the correct display in IE 9-. 58 | * 1. Add the correct display in IE. 59 | */ 60 | 61 | figcaption, 62 | figure, 63 | main { /* 1 */ 64 | display: block; 65 | } 66 | 67 | /** 68 | * Add the correct margin in IE 8. 69 | */ 70 | 71 | figure { 72 | margin: 1em 40px; 73 | } 74 | 75 | /** 76 | * 1. Add the correct box sizing in Firefox. 77 | * 2. Show the overflow in Edge and IE. 78 | */ 79 | 80 | hr { 81 | box-sizing: content-box; /* 1 */ 82 | height: 0; /* 1 */ 83 | overflow: visible; /* 2 */ 84 | } 85 | 86 | /** 87 | * 1. Correct the inheritance and scaling of font size in all browsers. 88 | * 2. Correct the odd `em` font sizing in all browsers. 89 | */ 90 | 91 | pre { 92 | font-family: monospace, monospace; /* 1 */ 93 | font-size: 1em; /* 2 */ 94 | } 95 | 96 | /* Text-level semantics 97 | ========================================================================== */ 98 | 99 | /** 100 | * 1. Remove the gray background on active links in IE 10. 101 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 102 | */ 103 | 104 | a { 105 | background-color: transparent; /* 1 */ 106 | -webkit-text-decoration-skip: objects; /* 2 */ 107 | } 108 | 109 | /** 110 | * Remove the outline on focused links when they are also active or hovered 111 | * in all browsers (opinionated). 112 | */ 113 | 114 | a:active, 115 | a:hover { 116 | outline-width: 0; 117 | } 118 | 119 | /** 120 | * 1. Remove the bottom border in Firefox 39-. 121 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 122 | */ 123 | 124 | abbr[title] { 125 | border-bottom: none; /* 1 */ 126 | text-decoration: underline; /* 2 */ 127 | text-decoration: underline dotted; /* 2 */ 128 | } 129 | 130 | /** 131 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 132 | */ 133 | 134 | b, 135 | strong { 136 | font-weight: inherit; 137 | } 138 | 139 | /** 140 | * Add the correct font weight in Chrome, Edge, and Safari. 141 | */ 142 | 143 | b, 144 | strong { 145 | font-weight: bolder; 146 | } 147 | 148 | /** 149 | * 1. Correct the inheritance and scaling of font size in all browsers. 150 | * 2. Correct the odd `em` font sizing in all browsers. 151 | */ 152 | 153 | code, 154 | kbd, 155 | samp { 156 | font-family: monospace, monospace; /* 1 */ 157 | font-size: 1em; /* 2 */ 158 | } 159 | 160 | /** 161 | * Add the correct font style in Android 4.3-. 162 | */ 163 | 164 | dfn { 165 | font-style: italic; 166 | } 167 | 168 | /** 169 | * Add the correct background and color in IE 9-. 170 | */ 171 | 172 | mark { 173 | background-color: #ff0; 174 | color: #000; 175 | } 176 | 177 | /** 178 | * Add the correct font size in all browsers. 179 | */ 180 | 181 | small { 182 | font-size: 80%; 183 | } 184 | 185 | /** 186 | * Prevent `sub` and `sup` elements from affecting the line height in 187 | * all browsers. 188 | */ 189 | 190 | sub, 191 | sup { 192 | font-size: 75%; 193 | line-height: 0; 194 | position: relative; 195 | vertical-align: baseline; 196 | } 197 | 198 | sub { 199 | bottom: -0.25em; 200 | } 201 | 202 | sup { 203 | top: -0.5em; 204 | } 205 | 206 | /* Embedded content 207 | ========================================================================== */ 208 | 209 | /** 210 | * Add the correct display in IE 9-. 211 | */ 212 | 213 | audio, 214 | video { 215 | display: inline-block; 216 | } 217 | 218 | /** 219 | * Add the correct display in iOS 4-7. 220 | */ 221 | 222 | audio:not([controls]) { 223 | display: none; 224 | height: 0; 225 | } 226 | 227 | /** 228 | * Remove the border on images inside links in IE 10-. 229 | */ 230 | 231 | img { 232 | border-style: none; 233 | } 234 | 235 | /** 236 | * Hide the overflow in IE. 237 | */ 238 | 239 | svg:not(:root) { 240 | overflow: hidden; 241 | } 242 | 243 | /* Forms 244 | ========================================================================== */ 245 | 246 | /** 247 | * 1. Change font properties to `inherit` in all browsers (opinionated). 248 | * 2. Remove the margin in Firefox and Safari. 249 | */ 250 | 251 | button, 252 | input, 253 | optgroup, 254 | select, 255 | textarea { 256 | font: inherit; /* 1 */ 257 | margin: 0; /* 2 */ 258 | } 259 | 260 | /** 261 | * Restore the font weight unset by the previous rule. 262 | */ 263 | 264 | optgroup { 265 | font-weight: bold; 266 | } 267 | 268 | /** 269 | * Show the overflow in IE. 270 | * 1. Show the overflow in Edge. 271 | */ 272 | 273 | button, 274 | input { /* 1 */ 275 | overflow: visible; 276 | } 277 | 278 | /** 279 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 280 | * 1. Remove the inheritance of text transform in Firefox. 281 | */ 282 | 283 | button, 284 | select { /* 1 */ 285 | text-transform: none; 286 | } 287 | 288 | /** 289 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 290 | * controls in Android 4. 291 | * 2. Correct the inability to style clickable types in iOS and Safari. 292 | */ 293 | 294 | button, 295 | html [type="button"], /* 1 */ 296 | [type="reset"], 297 | [type="submit"] { 298 | -webkit-appearance: button; /* 2 */ 299 | } 300 | 301 | /** 302 | * Remove the inner border and padding in Firefox. 303 | */ 304 | 305 | button::-moz-focus-inner, 306 | [type="button"]::-moz-focus-inner, 307 | [type="reset"]::-moz-focus-inner, 308 | [type="submit"]::-moz-focus-inner { 309 | border-style: none; 310 | padding: 0; 311 | } 312 | 313 | /** 314 | * Restore the focus styles unset by the previous rule. 315 | */ 316 | 317 | button:-moz-focusring, 318 | [type="button"]:-moz-focusring, 319 | [type="reset"]:-moz-focusring, 320 | [type="submit"]:-moz-focusring { 321 | outline: 1px dotted ButtonText; 322 | } 323 | 324 | /** 325 | * Change the border, margin, and padding in all browsers (opinionated). 326 | */ 327 | 328 | fieldset { 329 | border: 1px solid #c0c0c0; 330 | margin: 0 2px; 331 | padding: 0.35em 0.625em 0.75em; 332 | } 333 | 334 | /** 335 | * 1. Correct the text wrapping in Edge and IE. 336 | * 2. Correct the color inheritance from `fieldset` elements in IE. 337 | * 3. Remove the padding so developers are not caught out when they zero out 338 | * `fieldset` elements in all browsers. 339 | */ 340 | 341 | legend { 342 | box-sizing: border-box; /* 1 */ 343 | color: inherit; /* 2 */ 344 | display: table; /* 1 */ 345 | max-width: 100%; /* 1 */ 346 | padding: 0; /* 3 */ 347 | white-space: normal; /* 1 */ 348 | } 349 | 350 | /** 351 | * 1. Add the correct display in IE 9-. 352 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. 353 | */ 354 | 355 | progress { 356 | display: inline-block; /* 1 */ 357 | vertical-align: baseline; /* 2 */ 358 | } 359 | 360 | /** 361 | * Remove the default vertical scrollbar in IE. 362 | */ 363 | 364 | textarea { 365 | overflow: auto; 366 | } 367 | 368 | /** 369 | * 1. Add the correct box sizing in IE 10-. 370 | * 2. Remove the padding in IE 10-. 371 | */ 372 | 373 | [type="checkbox"], 374 | [type="radio"] { 375 | box-sizing: border-box; /* 1 */ 376 | padding: 0; /* 2 */ 377 | } 378 | 379 | /** 380 | * Correct the cursor style of increment and decrement buttons in Chrome. 381 | */ 382 | 383 | [type="number"]::-webkit-inner-spin-button, 384 | [type="number"]::-webkit-outer-spin-button { 385 | height: auto; 386 | } 387 | 388 | /** 389 | * 1. Correct the odd appearance in Chrome and Safari. 390 | * 2. Correct the outline style in Safari. 391 | */ 392 | 393 | [type="search"] { 394 | -webkit-appearance: textfield; /* 1 */ 395 | outline-offset: -2px; /* 2 */ 396 | } 397 | 398 | /** 399 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 400 | */ 401 | 402 | [type="search"]::-webkit-search-cancel-button, 403 | [type="search"]::-webkit-search-decoration { 404 | -webkit-appearance: none; 405 | } 406 | 407 | /** 408 | * 1. Correct the inability to style clickable types in iOS and Safari. 409 | * 2. Change font properties to `inherit` in Safari. 410 | */ 411 | 412 | ::-webkit-file-upload-button { 413 | -webkit-appearance: button; /* 1 */ 414 | font: inherit; /* 2 */ 415 | } 416 | 417 | /* Interactive 418 | ========================================================================== */ 419 | 420 | /* 421 | * Add the correct display in IE 9-. 422 | * 1. Add the correct display in Edge, IE, and Firefox. 423 | */ 424 | 425 | details, /* 1 */ 426 | menu { 427 | display: block; 428 | } 429 | 430 | /* 431 | * Add the correct display in all browsers. 432 | */ 433 | 434 | summary { 435 | display: list-item; 436 | } 437 | 438 | /* Scripting 439 | ========================================================================== */ 440 | 441 | /** 442 | * Add the correct display in IE 9-. 443 | */ 444 | 445 | canvas { 446 | display: inline-block; 447 | } 448 | 449 | /** 450 | * Add the correct display in IE. 451 | */ 452 | 453 | template { 454 | display: none; 455 | } 456 | 457 | /* Hidden 458 | ========================================================================== */ 459 | 460 | /** 461 | * Add the correct display in IE 10-. 462 | */ 463 | 464 | [hidden] { 465 | display: none; 466 | } -------------------------------------------------------------------------------- /src/assets/js/zepto/event.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var _zid = 1, undefined, 7 | slice = Array.prototype.slice, 8 | isFunction = $.isFunction, 9 | isString = function(obj){ return typeof obj == 'string' }, 10 | handlers = {}, 11 | specialEvents={}, 12 | focusinSupported = 'onfocusin' in window, 13 | focus = { focus: 'focusin', blur: 'focusout' }, 14 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } 15 | 16 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 17 | 18 | function zid(element) { 19 | return element._zid || (element._zid = _zid++) 20 | } 21 | function findHandlers(element, event, fn, selector) { 22 | event = parse(event) 23 | if (event.ns) var matcher = matcherFor(event.ns) 24 | return (handlers[zid(element)] || []).filter(function(handler) { 25 | return handler 26 | && (!event.e || handler.e == event.e) 27 | && (!event.ns || matcher.test(handler.ns)) 28 | && (!fn || zid(handler.fn) === zid(fn)) 29 | && (!selector || handler.sel == selector) 30 | }) 31 | } 32 | function parse(event) { 33 | var parts = ('' + event).split('.') 34 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')} 35 | } 36 | function matcherFor(ns) { 37 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 38 | } 39 | 40 | function eventCapture(handler, captureSetting) { 41 | return handler.del && 42 | (!focusinSupported && (handler.e in focus)) || 43 | !!captureSetting 44 | } 45 | 46 | function realEvent(type) { 47 | return hover[type] || (focusinSupported && focus[type]) || type 48 | } 49 | 50 | function add(element, events, fn, data, selector, delegator, capture){ 51 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])) 52 | events.split(/\s/).forEach(function(event){ 53 | if (event == 'ready') return $(document).ready(fn) 54 | var handler = parse(event) 55 | handler.fn = fn 56 | handler.sel = selector 57 | // emulate mouseenter, mouseleave 58 | if (handler.e in hover) fn = function(e){ 59 | var related = e.relatedTarget 60 | if (!related || (related !== this && !$.contains(this, related))) 61 | return handler.fn.apply(this, arguments) 62 | } 63 | handler.del = delegator 64 | var callback = delegator || fn 65 | handler.proxy = function(e){ 66 | e = compatible(e) 67 | if (e.isImmediatePropagationStopped()) return 68 | e.data = data 69 | var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) 70 | if (result === false) e.preventDefault(), e.stopPropagation() 71 | return result 72 | } 73 | handler.i = set.length 74 | set.push(handler) 75 | if ('addEventListener' in element) 76 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 77 | }) 78 | } 79 | function remove(element, events, fn, selector, capture){ 80 | var id = zid(element) 81 | ;(events || '').split(/\s/).forEach(function(event){ 82 | findHandlers(element, event, fn, selector).forEach(function(handler){ 83 | delete handlers[id][handler.i] 84 | if ('removeEventListener' in element) 85 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 86 | }) 87 | }) 88 | } 89 | 90 | $.event = { add: add, remove: remove } 91 | 92 | $.proxy = function(fn, context) { 93 | var args = (2 in arguments) && slice.call(arguments, 2) 94 | if (isFunction(fn)) { 95 | var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } 96 | proxyFn._zid = zid(fn) 97 | return proxyFn 98 | } else if (isString(context)) { 99 | if (args) { 100 | args.unshift(fn[context], fn) 101 | return $.proxy.apply(null, args) 102 | } else { 103 | return $.proxy(fn[context], fn) 104 | } 105 | } else { 106 | throw new TypeError("expected function") 107 | } 108 | } 109 | 110 | $.fn.bind = function(event, data, callback){ 111 | return this.on(event, data, callback) 112 | } 113 | $.fn.unbind = function(event, callback){ 114 | return this.off(event, callback) 115 | } 116 | $.fn.one = function(event, selector, data, callback){ 117 | return this.on(event, selector, data, callback, 1) 118 | } 119 | 120 | var returnTrue = function(){return true}, 121 | returnFalse = function(){return false}, 122 | ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/, 123 | eventMethods = { 124 | preventDefault: 'isDefaultPrevented', 125 | stopImmediatePropagation: 'isImmediatePropagationStopped', 126 | stopPropagation: 'isPropagationStopped' 127 | } 128 | 129 | function compatible(event, source) { 130 | if (source || !event.isDefaultPrevented) { 131 | source || (source = event) 132 | 133 | $.each(eventMethods, function(name, predicate) { 134 | var sourceMethod = source[name] 135 | event[name] = function(){ 136 | this[predicate] = returnTrue 137 | return sourceMethod && sourceMethod.apply(source, arguments) 138 | } 139 | event[predicate] = returnFalse 140 | }) 141 | 142 | event.timeStamp || (event.timeStamp = Date.now()) 143 | 144 | if (source.defaultPrevented !== undefined ? source.defaultPrevented : 145 | 'returnValue' in source ? source.returnValue === false : 146 | source.getPreventDefault && source.getPreventDefault()) 147 | event.isDefaultPrevented = returnTrue 148 | } 149 | return event 150 | } 151 | 152 | function createProxy(event) { 153 | var key, proxy = { originalEvent: event } 154 | for (key in event) 155 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] 156 | 157 | return compatible(proxy, event) 158 | } 159 | 160 | $.fn.delegate = function(selector, event, callback){ 161 | return this.on(event, selector, callback) 162 | } 163 | $.fn.undelegate = function(selector, event, callback){ 164 | return this.off(event, selector, callback) 165 | } 166 | 167 | $.fn.live = function(event, callback){ 168 | $(document.body).delegate(this.selector, event, callback) 169 | return this 170 | } 171 | $.fn.die = function(event, callback){ 172 | $(document.body).undelegate(this.selector, event, callback) 173 | return this 174 | } 175 | 176 | $.fn.on = function(event, selector, data, callback, one){ 177 | var autoRemove, delegator, $this = this 178 | if (event && !isString(event)) { 179 | $.each(event, function(type, fn){ 180 | $this.on(type, selector, data, fn, one) 181 | }) 182 | return $this 183 | } 184 | 185 | if (!isString(selector) && !isFunction(callback) && callback !== false) 186 | callback = data, data = selector, selector = undefined 187 | if (callback === undefined || data === false) 188 | callback = data, data = undefined 189 | 190 | if (callback === false) callback = returnFalse 191 | 192 | return $this.each(function(_, element){ 193 | if (one) autoRemove = function(e){ 194 | remove(element, e.type, callback) 195 | return callback.apply(this, arguments) 196 | } 197 | 198 | if (selector) delegator = function(e){ 199 | var evt, match = $(e.target).closest(selector, element).get(0) 200 | if (match && match !== element) { 201 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) 202 | return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) 203 | } 204 | } 205 | 206 | add(element, event, callback, data, selector, delegator || autoRemove) 207 | }) 208 | } 209 | $.fn.off = function(event, selector, callback){ 210 | var $this = this 211 | if (event && !isString(event)) { 212 | $.each(event, function(type, fn){ 213 | $this.off(type, selector, fn) 214 | }) 215 | return $this 216 | } 217 | 218 | if (!isString(selector) && !isFunction(callback) && callback !== false) 219 | callback = selector, selector = undefined 220 | 221 | if (callback === false) callback = returnFalse 222 | 223 | return $this.each(function(){ 224 | remove(this, event, callback, selector) 225 | }) 226 | } 227 | 228 | $.fn.trigger = function(event, args){ 229 | event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) 230 | event._args = args 231 | return this.each(function(){ 232 | // handle focus(), blur() by calling them directly 233 | if (event.type in focus && typeof this[event.type] == "function") this[event.type]() 234 | // items in the collection might not be DOM elements 235 | else if ('dispatchEvent' in this) this.dispatchEvent(event) 236 | else $(this).triggerHandler(event, args) 237 | }) 238 | } 239 | 240 | // triggers event handlers on current element just as if an event occurred, 241 | // doesn't trigger an actual event, doesn't bubble 242 | $.fn.triggerHandler = function(event, args){ 243 | var e, result 244 | this.each(function(i, element){ 245 | e = createProxy(isString(event) ? $.Event(event) : event) 246 | e._args = args 247 | e.target = element 248 | $.each(findHandlers(element, event.type || event), function(i, handler){ 249 | result = handler.proxy(e) 250 | if (e.isImmediatePropagationStopped()) return false 251 | }) 252 | }) 253 | return result 254 | } 255 | 256 | // shortcut methods for `.bind(event, fn)` for each event type 257 | ;('focusin focusout focus blur load resize scroll unload click dblclick '+ 258 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 259 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 260 | $.fn[event] = function(callback) { 261 | return (0 in arguments) ? 262 | this.bind(event, callback) : 263 | this.trigger(event) 264 | } 265 | }) 266 | 267 | $.Event = function(type, props) { 268 | if (!isString(type)) props = type, type = props.type 269 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true 270 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) 271 | event.initEvent(type, bubbles, true) 272 | return compatible(event) 273 | } 274 | 275 | })(Zepto) 276 | -------------------------------------------------------------------------------- /static/js/qq-wechat-emotion-parser.js: -------------------------------------------------------------------------------- 1 | 2 | !function(){window._qqWechatEmotionParser = {}; 3 | window.qqWechatEmotionParser = function() {}; 4 | }(); 5 | 6 | !function(){window._qqWechatEmotionParser.emotion_map={"/::)":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/0.gif","/::~":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/1.gif","/::B":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/2.gif","/::|":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/3.gif","/:8-)":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/4.gif","/::<":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/5.gif","/::$":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/6.gif","/::X":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/7.gif","/::Z":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/8.gif","/::'(":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/9.gif","/::-|":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/10.gif","/::@":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/11.gif","/::P":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/12.gif","/::D":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/13.gif","/::O":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/14.gif","/::(":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/15.gif","/::+":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/16.gif","/:--b":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/17.gif","/::Q":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/18.gif","/::T":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/19.gif","/:,@P":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/20.gif","/:,@-D":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/21.gif","/::d":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/22.gif","/:,@o":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/23.gif","/::g":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/24.gif","/:|-)":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/25.gif","/::!":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/26.gif","/::L":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/27.gif","/::>":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/28.gif","/::,@":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/29.gif","/:,@f":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/30.gif","/::-S":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/31.gif","/:?":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/32.gif","/:,@x":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/33.gif","/:,@@":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/34.gif","/::8":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/35.gif","/:,@!":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/36.gif","/:!!!":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/37.gif","/:xx":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/38.gif","/:bye":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/39.gif","/:wipe":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/40.gif","/:dig":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/41.gif","/:handclap":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/42.gif","/:&-(":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/43.gif","/:B-)":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/44.gif","/:<@":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/45.gif","/:@>":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/46.gif","/::-O":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/47.gif","/:>-|":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/48.gif","/:P-(":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/49.gif","/::'|":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/50.gif","/:X-)":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/51.gif","/::*":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/52.gif","/:@x":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/53.gif","/:8*":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/54.gif","/:pd":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/55.gif","/:":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/56.gif","/:beer":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/57.gif","/:basketb":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/58.gif","/:oo":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/59.gif","/:coffee":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/60.gif","/:eat":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/61.gif","/:pig":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/62.gif","/:rose":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/63.gif","/:fade":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/64.gif","/:showlove":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/65.gif","/:heart":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/66.gif","/:break":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/67.gif","/:cake":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/68.gif","/:li":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/69.gif","/:bome":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/70.gif","/:kn":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/71.gif","/:footb":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/72.gif","/:ladybug":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/73.gif","/:shit":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/74.gif","/:moon":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/75.gif","/:sun":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/76.gif","/:gift":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/77.gif","/:hug":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/78.gif","/:strong":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/79.gif","/:weak":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/80.gif","/:share":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/81.gif","/:v":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/82.gif","/:@)":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/83.gif","/:jj":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/84.gif","/:@@":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/85.gif","/:bad":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/86.gif","/:lvu":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/87.gif","/:no":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/88.gif","/:ok":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/89.gif","/:love":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/90.gif","/:":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/91.gif","/:jump":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/92.gif","/:shake":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/93.gif","/:":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/94.gif","/:circle":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/95.gif","/:kotow":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/96.gif","/:turn":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/97.gif","/:skip":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/98.gif","/:oY":"https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/99.gif"};}(); 7 | 8 | !function(){function Trie(){ 9 | this.words = 0; 10 | this.empty = 1; 11 | this.index = 0; 12 | this.children = {}; 13 | } 14 | 15 | Trie.prototype = { 16 | insert: function(str, pos, idx){ 17 | if(str.length === 0) { 18 | return; 19 | } 20 | var T = this; 21 | var k; 22 | var child; 23 | 24 | if(pos === undefined) { 25 | pos = 0; 26 | } 27 | if(pos === str.length) { 28 | T.index = idx; 29 | return; 30 | } 31 | k = str[pos]; 32 | if(T.children[k] === undefined){ 33 | T.children[k] = new Trie(); 34 | T.empty = 0; 35 | T.children[k].words = this.words + 1; 36 | } 37 | child = T.children[k]; 38 | child.insert(str, pos + 1, idx); 39 | }, 40 | 41 | build: function(arr){ 42 | var len = arr.length; 43 | for(var i = 0; i < len; i++){ 44 | this.insert(arr[i], 0, i); 45 | } 46 | }, 47 | 48 | searchOne: function(str, pos){ 49 | if(pos === undefined){ 50 | pos = 0; 51 | } 52 | var result = {}; 53 | if(str.length === 0) return result; 54 | var T = this; 55 | var child; 56 | var k; 57 | result.arr = []; 58 | k = str[pos]; 59 | child = T.children[k]; 60 | if(child !== undefined && pos < str.length){ 61 | return child.searchOne(str, pos + 1); 62 | } 63 | if(child === undefined && T.empty === 0) return result; 64 | if(T.empty == 1){ 65 | result.arr[0] = pos - T.words; 66 | result.arr[1] = T.index; 67 | result.words = T.words; 68 | return result; 69 | } 70 | return result; 71 | }, 72 | 73 | search: function(str){ 74 | if(this.empty == 1) return []; 75 | var len = str.length; 76 | var searchResult = []; 77 | var tmp; 78 | for(var i = 0; i < len - 1; i++){ 79 | tmp = this.searchOne(str, i); 80 | if(typeof tmp.arr !== 'undefined' && tmp.arr.length > 0){ 81 | searchResult.push(tmp.arr); 82 | i = i + tmp.words - 1; 83 | } 84 | } 85 | return searchResult; 86 | } 87 | }; 88 | 89 | if(typeof module !== 'undefined'){ 90 | module.exports = Trie; 91 | } 92 | else if(typeof window !== 'undefined'){ 93 | window._qqWechatEmotionParser.Trie = Trie; 94 | } 95 | 96 | }(); 97 | 98 | !function(){var emotion_map, trie, emotion_list, Trie; 99 | 100 | if(typeof module !== 'undefined'){ 101 | emotion_map = require('./emotions.json'); 102 | Trie = require('./trie'); 103 | build(); 104 | module.exports = qqWechatEmotionParser; 105 | } 106 | else if(window !== 'undefined'){ 107 | emotion_map = window._qqWechatEmotionParser.emotion_map; 108 | Trie = window._qqWechatEmotionParser.Trie; 109 | build(); 110 | window.qqWechatEmotionParser = qqWechatEmotionParser; 111 | } 112 | else return; 113 | 114 | function build(){ 115 | emotion_list = keys(emotion_map); 116 | trie = new Trie(); 117 | trie.build(emotion_list); 118 | } 119 | 120 | function qqWechatEmotionParser(str) { 121 | var indices = trie.search(str); 122 | indices.reverse().map(function(idx) { 123 | var pos = idx[0], 124 | emotion = emotion_list[idx[1]], 125 | img = '' + emotion + ''; 126 | str = splice(str, pos, emotion.length, img); 127 | }); 128 | return str; 129 | } 130 | 131 | function splice(str, index, count, add) { 132 | return str.slice(0, index) + add + str.slice(index + count); 133 | } 134 | 135 | function keys(map){ 136 | var list = []; 137 | for (var k in map) { 138 | if (map.hasOwnProperty(k)) list.push(k); 139 | } 140 | return list; 141 | } 142 | }(); 143 | -------------------------------------------------------------------------------- /src/components/MessageItem.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 196 | 197 | 419 | -------------------------------------------------------------------------------- /src/assets/js/zepto/ajax.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2016 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var jsonpID = +new Date(), 7 | document = window.document, 8 | key, 9 | name, 10 | rscript = /)<[^<]*)*<\/script>/gi, 11 | scriptTypeRE = /^(?:text|application)\/javascript/i, 12 | xmlTypeRE = /^(?:text|application)\/xml/i, 13 | jsonType = 'application/json', 14 | htmlType = 'text/html', 15 | blankRE = /^\s*$/, 16 | originAnchor = document.createElement('a') 17 | 18 | originAnchor.href = window.location.href 19 | 20 | // trigger a custom event and return false if it was cancelled 21 | function triggerAndReturn(context, eventName, data) { 22 | var event = $.Event(eventName) 23 | $(context).trigger(event, data) 24 | return !event.isDefaultPrevented() 25 | } 26 | 27 | // trigger an Ajax "global" event 28 | function triggerGlobal(settings, context, eventName, data) { 29 | if (settings.global) return triggerAndReturn(context || document, eventName, data) 30 | } 31 | 32 | // Number of active Ajax requests 33 | $.active = 0 34 | 35 | function ajaxStart(settings) { 36 | if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') 37 | } 38 | function ajaxStop(settings) { 39 | if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') 40 | } 41 | 42 | // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable 43 | function ajaxBeforeSend(xhr, settings) { 44 | var context = settings.context 45 | if (settings.beforeSend.call(context, xhr, settings) === false || 46 | triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) 47 | return false 48 | 49 | triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) 50 | } 51 | function ajaxSuccess(data, xhr, settings, deferred) { 52 | var context = settings.context, status = 'success' 53 | settings.success.call(context, data, status, xhr) 54 | if (deferred) deferred.resolveWith(context, [data, status, xhr]) 55 | triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) 56 | ajaxComplete(status, xhr, settings) 57 | } 58 | // type: "timeout", "error", "abort", "parsererror" 59 | function ajaxError(error, type, xhr, settings, deferred) { 60 | var context = settings.context 61 | settings.error.call(context, xhr, type, error) 62 | if (deferred) deferred.rejectWith(context, [xhr, type, error]) 63 | triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) 64 | ajaxComplete(type, xhr, settings) 65 | } 66 | // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" 67 | function ajaxComplete(status, xhr, settings) { 68 | var context = settings.context 69 | settings.complete.call(context, xhr, status) 70 | triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) 71 | ajaxStop(settings) 72 | } 73 | 74 | function ajaxDataFilter(data, type, settings) { 75 | if (settings.dataFilter == empty) return data 76 | var context = settings.context 77 | return settings.dataFilter.call(context, data, type) 78 | } 79 | 80 | // Empty function, used as default callback 81 | function empty() {} 82 | 83 | $.ajaxJSONP = function(options, deferred){ 84 | if (!('type' in options)) return $.ajax(options) 85 | 86 | var _callbackName = options.jsonpCallback, 87 | callbackName = ($.isFunction(_callbackName) ? 88 | _callbackName() : _callbackName) || ('Zepto' + (jsonpID++)), 89 | script = document.createElement('script'), 90 | originalCallback = window[callbackName], 91 | responseData, 92 | abort = function(errorType) { 93 | $(script).triggerHandler('error', errorType || 'abort') 94 | }, 95 | xhr = { abort: abort }, abortTimeout 96 | 97 | if (deferred) deferred.promise(xhr) 98 | 99 | $(script).on('load error', function(e, errorType){ 100 | clearTimeout(abortTimeout) 101 | $(script).off().remove() 102 | 103 | if (e.type == 'error' || !responseData) { 104 | ajaxError(null, errorType || 'error', xhr, options, deferred) 105 | } else { 106 | ajaxSuccess(responseData[0], xhr, options, deferred) 107 | } 108 | 109 | window[callbackName] = originalCallback 110 | if (responseData && $.isFunction(originalCallback)) 111 | originalCallback(responseData[0]) 112 | 113 | originalCallback = responseData = undefined 114 | }) 115 | 116 | if (ajaxBeforeSend(xhr, options) === false) { 117 | abort('abort') 118 | return xhr 119 | } 120 | 121 | window[callbackName] = function(){ 122 | responseData = arguments 123 | } 124 | 125 | script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) 126 | document.head.appendChild(script) 127 | 128 | if (options.timeout > 0) abortTimeout = setTimeout(function(){ 129 | abort('timeout') 130 | }, options.timeout) 131 | 132 | return xhr 133 | } 134 | 135 | $.ajaxSettings = { 136 | // Default type of request 137 | type: 'GET', 138 | // Callback that is executed before request 139 | beforeSend: empty, 140 | // Callback that is executed if the request succeeds 141 | success: empty, 142 | // Callback that is executed the the server drops error 143 | error: empty, 144 | // Callback that is executed on request complete (both: error and success) 145 | complete: empty, 146 | // The context for the callbacks 147 | context: null, 148 | // Whether to trigger "global" Ajax events 149 | global: true, 150 | // Transport 151 | xhr: function () { 152 | return new window.XMLHttpRequest() 153 | }, 154 | // MIME types mapping 155 | // IIS returns Javascript as "application/x-javascript" 156 | accepts: { 157 | script: 'text/javascript, application/javascript, application/x-javascript', 158 | json: jsonType, 159 | xml: 'application/xml, text/xml', 160 | html: htmlType, 161 | text: 'text/plain' 162 | }, 163 | // Whether the request is to another domain 164 | crossDomain: false, 165 | // Default timeout 166 | timeout: 0, 167 | // Whether data should be serialized to string 168 | processData: true, 169 | // Whether the browser should be allowed to cache GET responses 170 | cache: true, 171 | //Used to handle the raw response data of XMLHttpRequest. 172 | //This is a pre-filtering function to sanitize the response. 173 | //The sanitized response should be returned 174 | dataFilter: empty 175 | } 176 | 177 | function mimeToDataType(mime) { 178 | if (mime) mime = mime.split(';', 2)[0] 179 | return mime && ( mime == htmlType ? 'html' : 180 | mime == jsonType ? 'json' : 181 | scriptTypeRE.test(mime) ? 'script' : 182 | xmlTypeRE.test(mime) && 'xml' ) || 'text' 183 | } 184 | 185 | function appendQuery(url, query) { 186 | if (query == '') return url 187 | return (url + '&' + query).replace(/[&?]{1,2}/, '?') 188 | } 189 | 190 | // serialize payload and append it to the URL for GET requests 191 | function serializeData(options) { 192 | if (options.processData && options.data && $.type(options.data) != "string") 193 | options.data = $.param(options.data, options.traditional) 194 | if (options.data && (!options.type || options.type.toUpperCase() == 'GET' || 'jsonp' == options.dataType)) 195 | options.url = appendQuery(options.url, options.data), options.data = undefined 196 | } 197 | 198 | $.ajax = function(options){ 199 | var settings = $.extend({}, options || {}), 200 | deferred = $.Deferred && $.Deferred(), 201 | urlAnchor, hashIndex 202 | for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] 203 | 204 | ajaxStart(settings) 205 | 206 | if (!settings.crossDomain) { 207 | urlAnchor = document.createElement('a') 208 | urlAnchor.href = settings.url 209 | // cleans up URL for .href (IE only), see https://github.com/madrobby/zepto/pull/1049 210 | urlAnchor.href = urlAnchor.href 211 | settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host) 212 | } 213 | 214 | if (!settings.url) settings.url = window.location.toString() 215 | if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex) 216 | serializeData(settings) 217 | 218 | var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) 219 | if (hasPlaceholder) dataType = 'jsonp' 220 | 221 | if (settings.cache === false || ( 222 | (!options || options.cache !== true) && 223 | ('script' == dataType || 'jsonp' == dataType) 224 | )) 225 | settings.url = appendQuery(settings.url, '_=' + Date.now()) 226 | 227 | if ('jsonp' == dataType) { 228 | if (!hasPlaceholder) 229 | settings.url = appendQuery(settings.url, 230 | settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') 231 | return $.ajaxJSONP(settings, deferred) 232 | } 233 | 234 | var mime = settings.accepts[dataType], 235 | headers = { }, 236 | setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, 237 | protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, 238 | xhr = settings.xhr(), 239 | nativeSetHeader = xhr.setRequestHeader, 240 | abortTimeout 241 | 242 | if (deferred) deferred.promise(xhr) 243 | 244 | if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') 245 | setHeader('Accept', mime || '*/*') 246 | if (mime = settings.mimeType || mime) { 247 | if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] 248 | xhr.overrideMimeType && xhr.overrideMimeType(mime) 249 | } 250 | if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) 251 | setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') 252 | 253 | if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) 254 | xhr.setRequestHeader = setHeader 255 | 256 | xhr.onreadystatechange = function(){ 257 | if (xhr.readyState == 4) { 258 | xhr.onreadystatechange = empty 259 | clearTimeout(abortTimeout) 260 | var result, error = false 261 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { 262 | dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) 263 | 264 | if (xhr.responseType == 'arraybuffer' || xhr.responseType == 'blob') 265 | result = xhr.response 266 | else { 267 | result = xhr.responseText 268 | 269 | try { 270 | // http://perfectionkills.com/global-eval-what-are-the-options/ 271 | // sanitize response accordingly if data filter callback provided 272 | result = ajaxDataFilter(result, dataType, settings) 273 | if (dataType == 'script') (1,eval)(result) 274 | else if (dataType == 'xml') result = xhr.responseXML 275 | else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) 276 | } catch (e) { error = e } 277 | 278 | if (error) return ajaxError(error, 'parsererror', xhr, settings, deferred) 279 | } 280 | 281 | ajaxSuccess(result, xhr, settings, deferred) 282 | } else { 283 | ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) 284 | } 285 | } 286 | } 287 | 288 | if (ajaxBeforeSend(xhr, settings) === false) { 289 | xhr.abort() 290 | ajaxError(null, 'abort', xhr, settings, deferred) 291 | return xhr 292 | } 293 | 294 | var async = 'async' in settings ? settings.async : true 295 | xhr.open(settings.type, settings.url, async, settings.username, settings.password) 296 | 297 | if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] 298 | 299 | for (name in headers) nativeSetHeader.apply(xhr, headers[name]) 300 | 301 | if (settings.timeout > 0) abortTimeout = setTimeout(function(){ 302 | xhr.onreadystatechange = empty 303 | xhr.abort() 304 | ajaxError(null, 'timeout', xhr, settings, deferred) 305 | }, settings.timeout) 306 | 307 | // avoid sending empty string (#319) 308 | xhr.send(settings.data ? settings.data : null) 309 | return xhr 310 | } 311 | 312 | // handle optional data/success arguments 313 | function parseArguments(url, data, success, dataType) { 314 | if ($.isFunction(data)) dataType = success, success = data, data = undefined 315 | if (!$.isFunction(success)) dataType = success, success = undefined 316 | return { 317 | url: url 318 | , data: data 319 | , success: success 320 | , dataType: dataType 321 | } 322 | } 323 | 324 | $.get = function(/* url, data, success, dataType */){ 325 | return $.ajax(parseArguments.apply(null, arguments)) 326 | } 327 | 328 | $.post = function(/* url, data, success, dataType */){ 329 | var options = parseArguments.apply(null, arguments) 330 | options.type = 'POST' 331 | return $.ajax(options) 332 | } 333 | 334 | $.getJSON = function(/* url, data, success */){ 335 | var options = parseArguments.apply(null, arguments) 336 | options.dataType = 'json' 337 | return $.ajax(options) 338 | } 339 | 340 | $.fn.load = function(url, data, success){ 341 | if (!this.length) return this 342 | var self = this, parts = url.split(/\s/), selector, 343 | options = parseArguments(url, data, success), 344 | callback = options.success 345 | if (parts.length > 1) options.url = parts[0], selector = parts[1] 346 | options.success = function(response){ 347 | self.html(selector ? 348 | $('
').html(response.replace(rscript, "")).find(selector) 349 | : response) 350 | callback && callback.apply(self, arguments) 351 | } 352 | $.ajax(options) 353 | return this 354 | } 355 | 356 | var escape = encodeURIComponent 357 | 358 | function serialize(params, obj, traditional, scope){ 359 | var type, array = $.isArray(obj), hash = $.isPlainObject(obj) 360 | $.each(obj, function(key, value) { 361 | type = $.type(value) 362 | if (scope) key = traditional ? scope : 363 | scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' 364 | // handle data in serializeArray() format 365 | if (!scope && array) params.add(value.name, value.value) 366 | // recurse into nested objects 367 | else if (type == "array" || (!traditional && type == "object")) 368 | serialize(params, value, traditional, key) 369 | else params.add(key, value) 370 | }) 371 | } 372 | 373 | $.param = function(obj, traditional){ 374 | var params = [] 375 | params.add = function(key, value) { 376 | if ($.isFunction(value)) value = value() 377 | if (value == null) value = "" 378 | this.push(escape(key) + '=' + escape(value)) 379 | } 380 | serialize(params, obj, traditional) 381 | return params.join('&').replace(/%20/g, '+') 382 | } 383 | })(Zepto) 384 | -------------------------------------------------------------------------------- /src/views/Chat.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 347 | 348 | 481 | --------------------------------------------------------------------------------