├── api ├── env.js ├── auth.js └── department.js ├── views ├── index.jade ├── error.jade └── layout.jade ├── README.md ├── package.json ├── public ├── stylesheets │ └── style.css └── javascripts │ ├── demo.js │ └── zepto.min.js ├── app.js ├── util ├── http.js └── sign.js ├── bin └── www └── routes └── index.js /api/env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | corpId: '', 3 | secret: '' 4 | }; -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | #log 6 | -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # open api demo (nodejs ver.) 2 | 3 | ## Awesome project 4 | 5 | ### Getting started 6 | 1. set up your own corp id and corp secret in env.js 7 | 2. launch your server in your prayers 8 | -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | script(type='text/javascript', src='/javascripts/zepto.min.js') 7 | script(type='text/javascript', src='/javascripts/demo.js') 8 | body 9 | block content -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openapidemo", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.12.4", 10 | "cookie-parser": "~1.3.5", 11 | "debug": "~2.2.0", 12 | "express": "~4.12.4", 13 | "jade": "~1.9.2", 14 | "morgan": "~1.5.3", 15 | "serve-favicon": "~2.2.1" 16 | } 17 | } -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | .log-i { 11 | color: green; 12 | } 13 | 14 | .log-e { 15 | color: red; 16 | } 17 | 18 | .tag { 19 | display: inline-block; 20 | width: 170px; 21 | } 22 | 23 | .api { 24 | display: inline-block; 25 | width: 300px; 26 | } -------------------------------------------------------------------------------- /api/auth.js: -------------------------------------------------------------------------------- 1 | var querystring = require("querystring"); 2 | var httpUtil = require('../util/http'); 3 | var env = require('./env'); 4 | 5 | module.exports = { 6 | 7 | getAccessToken: function(cb) { 8 | var path = '/gettoken?' + querystring.stringify({ 9 | corpid: env.corpId, 10 | corpsecret: env.secret 11 | }); 12 | httpUtil.get(path, cb); 13 | }, 14 | 15 | getTicket: function(accessToken, cb) { 16 | var path = '/get_jsapi_ticket?' + querystring.stringify({ 17 | type: 'jsapi', 18 | access_token: accessToken 19 | }); 20 | httpUtil.get(path, cb); 21 | }, 22 | }; -------------------------------------------------------------------------------- /api/department.js: -------------------------------------------------------------------------------- 1 | var querystring = require("querystring"); 2 | var httpUtil = require('../util/http'); 3 | 4 | module.exports = { 5 | 6 | create: function(accessToken, dept, cb) { 7 | var path = '/department/create?' + querystring.stringify({ 8 | access_token: accessToken, 9 | }); 10 | httpUtil.post(path, JSON.stringify(dept), cb); 11 | }, 12 | 13 | list: function(accessToken, cb) { 14 | var path = '/department/list?' + querystring.stringify({ 15 | access_token: accessToken, 16 | }); 17 | httpUtil.get(path, cb); 18 | }, 19 | 20 | delete: function(accessToken, id, cb) { 21 | var path = '/department/delete?' + querystring.stringify({ 22 | access_token: accessToken, 23 | id: id, 24 | }); 25 | httpUtil.get(path, cb); 26 | } 27 | }; -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'jade'); 15 | 16 | // uncomment after placing your favicon in /public 17 | //app.use(favicon(__dirname + '/public/favicon.ico')); 18 | app.use(logger('dev')); 19 | app.use(bodyParser.json()); 20 | app.use(bodyParser.urlencoded({ extended: false })); 21 | app.use(cookieParser()); 22 | app.use(express.static(path.join(__dirname, 'public'))); 23 | 24 | app.use('/', routes); 25 | 26 | // catch 404 and forward to error handler 27 | app.use(function(req, res, next) { 28 | var err = new Error('Not Found'); 29 | err.status = 404; 30 | next(err); 31 | }); 32 | 33 | // error handlers 34 | 35 | // development error handler 36 | // will print stacktrace 37 | if (app.get('env') === 'development') { 38 | app.use(function(err, req, res, next) { 39 | res.status(err.status || 500); 40 | res.render('error', { 41 | message: err.message, 42 | error: err 43 | }); 44 | }); 45 | } 46 | 47 | // production error handler 48 | // no stacktraces leaked to user 49 | app.use(function(err, req, res, next) { 50 | res.status(err.status || 500); 51 | res.render('error', { 52 | message: err.message, 53 | error: {} 54 | }); 55 | }); 56 | 57 | 58 | module.exports = app; 59 | -------------------------------------------------------------------------------- /util/http.js: -------------------------------------------------------------------------------- 1 | var https = require("https"); 2 | 3 | var oapiHost = 'oapi.dingtalk.com'; 4 | 5 | module.exports = { 6 | get: function(path, cb) { 7 | https.get('https://' + oapiHost + path, function(response) { 8 | if (response.statusCode === 200) { 9 | var body = ''; 10 | response.on('data', function (data) { 11 | body += data; 12 | }).on('end', function () { 13 | var result = JSON.parse(body); 14 | if (result && 0 === result.errcode) { 15 | cb.success(result); 16 | } 17 | else { 18 | cb.error(result); 19 | } 20 | }); 21 | } 22 | else { 23 | cb.error(response.statusCode); 24 | } 25 | }); 26 | }, 27 | 28 | post: function(path, data, cb) { 29 | var opt = { 30 | method: 'POST', 31 | headers: { 32 | 'Content-Type': 'application/json' 33 | }, 34 | host: oapiHost, 35 | path: path, 36 | }; 37 | var req = https.request(opt, function (response) { 38 | if (response.statusCode === 200) { 39 | var body = ''; 40 | response.on('data', function (data) { 41 | body += data; 42 | }).on('end', function () { 43 | var result = JSON.parse(body); 44 | if (result && 0 === result.errcode) { 45 | cb.success(result); 46 | } 47 | else { 48 | cb.error(result); 49 | } 50 | }); 51 | } 52 | else { 53 | cb.error(response.statusCode); 54 | } 55 | }); 56 | req.write(data + '\n'); 57 | req.end(); 58 | } 59 | }; -------------------------------------------------------------------------------- /util/sign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by liqiao on 7/15/15. 3 | */ 4 | 5 | var crypto = require('crypto'); 6 | 7 | var auth = require('../api/auth'); 8 | 9 | 10 | 11 | var sign = { 12 | getJsapiSign: function(params) { 13 | var plain = 'jsapi_ticket=' + params.ticket + '&noncestr=' + params.nonceStr + 14 | '×tamp=' + params.timeStamp + '&url=' + params.url; 15 | console.log(plain); 16 | var sha1 = crypto.createHash('sha1'); 17 | sha1.update(plain, 'utf8'); 18 | return sha1.digest('hex'); 19 | }, 20 | 21 | getSign: function(params, cb) { 22 | auth.getAccessToken({ 23 | success: function(data) { 24 | if (data && data.access_token) { 25 | accessToken = data.access_token; 26 | console.log('sign accessToken: ' + accessToken); 27 | auth.getTicket(accessToken, { 28 | success: function(data) { 29 | if (data && data.ticket) { 30 | jsapiTicket = data.ticket; 31 | console.log('sign ticket: ' + jsapiTicket); 32 | params.ticket = jsapiTicket; 33 | var signature = sign.getJsapiSign(params); 34 | console.log('sign signature:' + signature); 35 | cb.success(signature); 36 | } 37 | else { 38 | error('cannot get jsapi_ticket'); 39 | } 40 | }, 41 | error: cb.error 42 | }); 43 | } 44 | else { 45 | error('cannot get access_token'); 46 | } 47 | }, 48 | error: cb.error 49 | }); 50 | } 51 | }; 52 | 53 | module.exports = sign; 54 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('openapidemo:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /public/javascripts/demo.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', function() { 2 | var logger = $('#log'); 3 | 4 | logger.i = function(tag, api, msg) { 5 | this.append('
' + 6 | '' + tag + '' + 7 | '' + api + '' + 8 | ''+ msg + '
'); 9 | }; 10 | 11 | logger.e = function(tag, api, msg) { 12 | this.append('
' + 13 | '' + tag + '' + 14 | '' + api + '' + 15 | ''+ msg + '
'); 16 | }; 17 | 18 | run(logger); 19 | }); 20 | 21 | function run(logger) { 22 | get( 23 | 'getapis', 24 | function(data) { 25 | var apiMap = data.msg.map; 26 | var sequence = data.msg.sequence.reverse(); 27 | var next; 28 | for (var i in sequence) { 29 | var apiPath = sequence[i]; 30 | var apiTag = apiMap[apiPath].tag; 31 | next = (function(_apiPath, _apiTag, _next) { 32 | return function() { 33 | get(_apiPath, function(data) { 34 | if (data.err) { 35 | logger.e(_apiTag, _apiPath, data.err); 36 | } 37 | else { 38 | logger.i(_apiTag, _apiPath, data.msg); 39 | } 40 | if (_next) { 41 | _next(); 42 | } 43 | }, function(err) { 44 | logger.e(_api, _apiPath, err); 45 | }); 46 | }; 47 | }) (apiPath, apiTag, next); 48 | } 49 | next(); 50 | }, 51 | function(err) { 52 | logger.e('error: cannot get test api list'); 53 | }); 54 | } 55 | 56 | 57 | function get(url, onSuccess, onFail) { 58 | $.ajax({ 59 | url: url, 60 | type: 'GET', 61 | success: function(data, status, xhr) { 62 | onSuccess(JSON.parse(data)); 63 | }, 64 | error: function(xhr, errorType, error) { 65 | onFail(errorType + ', ' + error); 66 | } 67 | }); 68 | } -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var https = require('https'); 2 | var querystring = require('querystring'); 3 | var express = require('express'); 4 | var router = express.Router(); 5 | 6 | //apis 7 | var auth = require('../api/auth'); 8 | var department = require('../api/department'); 9 | 10 | //utils 11 | var sign = require('../util/sign'); 12 | 13 | var accessToken; 14 | var departmentId; 15 | var jsapiTicket; 16 | 17 | var apis = { 18 | 'auth/getAccessToken': { 19 | tag: '获取AccessToken', 20 | action: function(cb) { 21 | auth.getAccessToken({ 22 | success: function(data) { 23 | if (data && data.access_token) { 24 | accessToken = data.access_token; 25 | cb.success('OK'); 26 | } 27 | else { 28 | error('cannot get access_token'); 29 | } 30 | }, 31 | error: cb.error 32 | }); 33 | } 34 | }, 35 | 36 | 'department/create': { 37 | tag: '创建部门', 38 | action: function(cb) { 39 | var dept = { 40 | name: 'TestDeptNodeJs6', 41 | parentid: '1', 42 | order: '1' 43 | }; 44 | department.create(accessToken, dept, { 45 | success: function(data) { 46 | if (data) { 47 | departmentId = data.id; 48 | if (departmentId) { 49 | cb.success('OK. 新增部门id:' + departmentId); 50 | return; 51 | } 52 | } 53 | cb.error('cannot get departmentId: ' + data); 54 | }, 55 | error: cb.error 56 | }); 57 | } 58 | }, 59 | 60 | 'department/list': { 61 | tag: '获取部门列表', 62 | action: function(cb) { 63 | department.list(accessToken, { 64 | success: function(data) { 65 | cb.success('OK. 部门数量:' + data.department.length); 66 | }, 67 | error: cb.error 68 | }); 69 | } 70 | }, 71 | 72 | 'department/delete': { 73 | tag: '删除部门', 74 | action: function(cb) { 75 | department.delete(accessToken, departmentId, { 76 | success: function(data) { 77 | cb.success('OK. 删除部门id:' + departmentId); 78 | }, 79 | error: cb.error 80 | }); 81 | } 82 | }, 83 | 84 | 'auth/getTicket': { 85 | tag: '获取jsapi ticket', 86 | action: function(cb) { 87 | auth.getTicket(accessToken, { 88 | success: function(data) { 89 | if (data && data.ticket) { 90 | jsapiTicket = data.ticket; 91 | cb.success('OK. jsapi ticket:' + jsapiTicket); 92 | } 93 | else { 94 | error('cannot get jsapi_ticket'); 95 | } 96 | }, 97 | error: cb.error 98 | }); 99 | } 100 | }, 101 | 102 | 'sign/getJsapiSign': { 103 | tag: '获取signature', 104 | action: function(cb) { 105 | var signature = sign.getJsapiSign({ 106 | ticket: jsapiTicket, 107 | nonceStr: 'abcdefg', 108 | timeStamp: new Date().getTime(), 109 | url: "http://www.baidu.com" 110 | }); 111 | console.log('signature:' + signature); 112 | cb.success('OK. signature:' + signature); 113 | } 114 | } 115 | }; 116 | 117 | var apiMap = {}; 118 | var apiSequence = []; 119 | 120 | 121 | function addApi(apiName, api) { 122 | var apiPath = '/api/' + apiName; 123 | apiSequence.push(apiPath); 124 | apiMap[apiPath] = { 125 | tag: api.tag 126 | }; 127 | router.get(apiPath, function(req, res, next) { 128 | api.action({ 129 | success: function(data) { 130 | res.send({msg: JSON.stringify(data)}); 131 | }, 132 | error: function(message) { 133 | res.send({err: 'api error: ' + JSON.stringify(message)}); 134 | } 135 | }); 136 | }); 137 | } 138 | 139 | 140 | router.get('/', function(req, res, next) { 141 | res.render('index', { title: 'OpenApiDemo (Nodejs ver.)' }); 142 | }); 143 | 144 | router.get('/sign', function(req, res, next) { 145 | var params = { 146 | nonceStr: req.query.nonceStr, 147 | timeStamp: req.query.timeStamp, 148 | url: decodeURIComponent(req.query.url) 149 | }; 150 | sign.getSign(params, { 151 | success: function(data) { 152 | res.send(data); 153 | }, 154 | error: function(err) { 155 | res.send(err); 156 | } 157 | }); 158 | }); 159 | 160 | router.get('/getapis', function(req, res, next) { 161 | res.send({ 162 | msg : { 163 | map: apiMap, 164 | sequence: apiSequence 165 | } 166 | }); 167 | }); 168 | 169 | for (var api in apis) { 170 | addApi(api, apis[api]); 171 | } 172 | 173 | module.exports = router; -------------------------------------------------------------------------------- /public/javascripts/zepto.min.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.0rc1 - polyfill zepto event detect fx ajax form touch - zeptojs.com/license */ 2 | (function(a){String.prototype.trim===a&&(String.prototype.trim=function(){return this.replace(/^\s+/,"").replace(/\s+$/,"")}),Array.prototype.reduce===a&&(Array.prototype.reduce=function(b){if(this===void 0||this===null)throw new TypeError;var c=Object(this),d=c.length>>>0,e=0,f;if(typeof b!="function")throw new TypeError;if(d==0&&arguments.length==1)throw new TypeError;if(arguments.length>=2)f=arguments[1];else do{if(e in c){f=c[e++];break}if(++e>=d)throw new TypeError}while(!0);while(e0?[].concat.apply([],a):a}function H(a){return a.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function I(a){return a in i?i[a]:i[a]=new RegExp("(^|\\s)"+a+"(\\s|$)")}function J(a,b){return typeof b=="number"&&!k[H(a)]?b+"px":b}function K(a){var b,c;return h[a]||(b=g.createElement(a),g.body.appendChild(b),c=j(b,"").getPropertyValue("display"),b.parentNode.removeChild(b),c=="none"&&(c="block"),h[a]=c),h[a]}function L(b,d){return d===a?c(b):c(b).filter(d)}function M(a,b,c,d){return A(b)?b.call(a,c,d):b}function N(a,b,d){var e=a%2?b:b.parentNode;e?e.insertBefore(d,a?a==1?e.firstChild:a==2?b:null:b.nextSibling):c(d).remove()}function O(a,b){b(a);for(var c in a.childNodes)O(a.childNodes[c],b)}var a,b,c,d,e=[],f=e.slice,g=window.document,h={},i={},j=g.defaultView.getComputedStyle,k={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,m=[1,3,8,9,11],n=["after","prepend","before","append"],o=g.createElement("table"),p=g.createElement("tr"),q={tr:g.createElement("tbody"),tbody:o,thead:o,tfoot:o,td:p,th:p,"*":g.createElement("div")},r=/complete|loaded|interactive/,s=/^\.([\w-]+)$/,t=/^#([\w-]+)$/,u=/^[\w-]+$/,v={}.toString,w={},x,y,z=g.createElement("div");return w.matches=function(a,b){if(!a||a.nodeType!==1)return!1;var c=a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.matchesSelector;if(c)return c.call(a,b);var d,e=a.parentNode,f=!e;return f&&(e=z).appendChild(a),d=~w.qsa(e,b).indexOf(a),f&&z.removeChild(a),d},x=function(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})},y=function(a){return a.filter(function(b,c){return a.indexOf(b)==c})},w.fragment=function(b,d){d===a&&(d=l.test(b)&&RegExp.$1),d in q||(d="*");var e=q[d];return e.innerHTML=""+b,c.each(f.call(e.childNodes),function(){e.removeChild(this)})},w.Z=function(a,b){return a=a||[],a.__proto__=arguments.callee.prototype,a.selector=b||"",a},w.isZ=function(a){return a instanceof w.Z},w.init=function(b,d){if(!b)return w.Z();if(A(b))return c(g).ready(b);if(w.isZ(b))return b;var e;if(D(b))e=F(b);else if(C(b))e=[c.extend({},b)],b=null;else if(m.indexOf(b.nodeType)>=0||b===window)e=[b],b=null;else if(l.test(b))e=w.fragment(b.trim(),RegExp.$1),b=null;else{if(d!==a)return c(d).find(b);e=w.qsa(g,b)}return w.Z(e,b)},c=function(a,b){return w.init(a,b)},c.extend=function(c){return f.call(arguments,1).forEach(function(d){for(b in d)d[b]!==a&&(c[b]=d[b])}),c},w.qsa=function(a,b){var c;return a===g&&t.test(b)?(c=a.getElementById(RegExp.$1))?[c]:e:a.nodeType!==1&&a.nodeType!==9?e:f.call(s.test(b)?a.getElementsByClassName(RegExp.$1):u.test(b)?a.getElementsByTagName(b):a.querySelectorAll(b))},c.isFunction=A,c.isObject=B,c.isArray=D,c.isPlainObject=C,c.inArray=function(a,b,c){return e.indexOf.call(b,a,c)},c.trim=function(a){return a.trim()},c.uuid=0,c.map=function(a,b){var c,d=[],e,f;if(E(a))for(e=0;e0&&w.matches(this[0],a)},not:function(b){var d=[];if(A(b)&&b.call!==a)this.each(function(a){b.call(this,a)||d.push(this)});else{var e=typeof b=="string"?this.filter(b):E(b)&&A(b.item)?f.call(b):c(b);this.forEach(function(a){e.indexOf(a)<0&&d.push(a)})}return c(d)},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){var a=this[0];return a&&!B(a)?a:c(a)},last:function(){var a=this[this.length-1];return a&&!B(a)?a:c(a)},find:function(a){var b;return this.length==1?b=w.qsa(this[0],a):b=this.map(function(){return w.qsa(this,a)}),c(b)},closest:function(a,b){var d=this[0];while(d&&!w.matches(d,a))d=d!==b&&d!==g&&d.parentNode;return c(d)},parents:function(a){var b=[],d=this;while(d.length>0)d=c.map(d,function(a){if((a=a.parentNode)&&a!==g&&b.indexOf(a)<0)return b.push(a),a});return L(b,a)},parent:function(a){return L(y(this.pluck("parentNode")),a)},children:function(a){return L(this.map(function(){return f.call(this.children)}),a)},siblings:function(a){return L(this.map(function(a,b){return f.call(b.parentNode.children).filter(function(a){return a!==b})}),a)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(a){return this.map(function(){return this[a]})},show:function(){return this.each(function(){this.style.display=="none"&&(this.style.display=null),j(this,"").getPropertyValue("display")=="none"&&(this.style.display=K(this.nodeName))})},replaceWith:function(a){return this.before(a).remove()},wrap:function(a){return this.each(function(){c(this).wrapAll(c(a)[0].cloneNode(!1))})},wrapAll:function(a){return this[0]&&(c(this[0]).before(a=c(a)),a.append(this)),this},unwrap:function(){return this.parent().each(function(){c(this).replaceWith(c(this).children())}),this},clone:function(){return c(this.map(function(){return this.cloneNode(!0)}))},hide:function(){return this.css("display","none")},toggle:function(b){return(b===a?this.css("display")=="none":b)?this.show():this.hide()},prev:function(){return c(this.pluck("previousElementSibling"))},next:function(){return c(this.pluck("nextElementSibling"))},html:function(b){return b===a?this.length>0?this[0].innerHTML:null:this.each(function(a){var d=this.innerHTML;c(this).empty().append(M(this,b,a,d))})},text:function(b){return b===a?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=b})},attr:function(c,d){var e;return typeof c=="string"&&d===a?this.length==0||this[0].nodeType!==1?a:c=="value"&&this[0].nodeName=="INPUT"?this.val():!(e=this[0].getAttribute(c))&&c in this[0]?this[0][c]:e:this.each(function(a){if(this.nodeType!==1)return;if(B(c))for(b in c)this.setAttribute(b,c[b]);else this.setAttribute(c,M(this,d,a,this.getAttribute(c)))})},removeAttr:function(a){return this.each(function(){this.nodeType===1&&this.removeAttribute(a)})},prop:function(b,c){return c===a?this[0]?this[0][b]:a:this.each(function(a){this[b]=M(this,c,a,this[b])})},data:function(b,c){var d=this.attr("data-"+H(b),c);return d!==null?d:a},val:function(b){return b===a?this.length>0?this[0].value:a:this.each(function(a){this.value=M(this,b,a,this.value)})},offset:function(){if(this.length==0)return null;var a=this[0].getBoundingClientRect();return{left:a.left+window.pageXOffset,top:a.top+window.pageYOffset,width:a.width,height:a.height}},css:function(c,d){if(d===a&&typeof c=="string")return this.length==0?a:this[0].style[x(c)]||j(this[0],"").getPropertyValue(c);var e="";for(b in c)typeof c[b]=="string"&&c[b]==""?this.each(function(){this.style.removeProperty(H(b))}):e+=H(b)+":"+J(b,c[b])+";";return typeof c=="string"&&(d==""?this.each(function(){this.style.removeProperty(H(c))}):e=H(c)+":"+J(c,d)),this.each(function(){this.style.cssText+=";"+e})},index:function(a){return a?this.indexOf(c(a)[0]):this.parent().children().indexOf(this[0])},hasClass:function(a){return this.length<1?!1:I(a).test(this[0].className)},addClass:function(a){return this.each(function(b){d=[];var e=this.className,f=M(this,a,b,e);f.split(/\s+/g).forEach(function(a){c(this).hasClass(a)||d.push(a)},this),d.length&&(this.className+=(e?" ":"")+d.join(" "))})},removeClass:function(b){return this.each(function(c){if(b===a)return this.className="";d=this.className,M(this,b,c,d).split(/\s+/g).forEach(function(a){d=d.replace(I(a)," ")}),this.className=d.trim()})},toggleClass:function(b,d){return this.each(function(e){var f=M(this,b,e,this.className);(d===a?!c(this).hasClass(f):d)?c(this).addClass(f):c(this).removeClass(f)})}},["width","height"].forEach(function(b){c.fn[b]=function(d){var e,f=b.replace(/./,function(a){return a[0].toUpperCase()});return d===a?this[0]==window?window["inner"+f]:this[0]==g?g.documentElement["offset"+f]:(e=this.offset())&&e[b]:this.each(function(a){var e=c(this);e.css(b,M(this,d,a,e[b]()))})}}),n.forEach(function(a,b){c.fn[a]=function(){var a=c.map(arguments,function(a){return B(a)?a:w.fragment(a)});if(a.length<1)return this;var d=this.length,e=d>1,f=b<2;return this.each(function(c,g){for(var h=0;h0&&this.bind(o,n),setTimeout(function(){m.css(i),e<=0&&setTimeout(function(){m.each(function(){n.call(this)})},0)},0),this},i=null}(Zepto),function($){function triggerAndReturn(a,b,c){var d=$.Event(b);return $(a).trigger(d,c),!d.defaultPrevented}function triggerGlobal(a,b,c,d){if(a.global)return triggerAndReturn(b||document,c,d)}function ajaxStart(a){a.global&&$.active++===0&&triggerGlobal(a,null,"ajaxStart")}function ajaxStop(a){a.global&&!--$.active&&triggerGlobal(a,null,"ajaxStop")}function ajaxBeforeSend(a,b){var c=b.context;if(b.beforeSend.call(c,a,b)===!1||triggerGlobal(b,c,"ajaxBeforeSend",[a,b])===!1)return!1;triggerGlobal(b,c,"ajaxSend",[a,b])}function ajaxSuccess(a,b,c){var d=c.context,e="success";c.success.call(d,a,e,b),triggerGlobal(c,d,"ajaxSuccess",[b,c,a]),ajaxComplete(e,b,c)}function ajaxError(a,b,c,d){var e=d.context;d.error.call(e,c,b,a),triggerGlobal(d,e,"ajaxError",[c,d,a]),ajaxComplete(b,c,d)}function ajaxComplete(a,b,c){var d=c.context;c.complete.call(d,b,a),triggerGlobal(c,d,"ajaxComplete",[b,c]),ajaxStop(c)}function empty(){}function mimeToDataType(a){return a&&(a==htmlType?"html":a==jsonType?"json":scriptTypeRE.test(a)?"script":xmlTypeRE.test(a)&&"xml")||"text"}function appendQuery(a,b){return(a+"&"+b).replace(/[&?]{1,2}/,"?")}function serializeData(a){isObject(a.data)&&(a.data=$.param(a.data)),a.data&&(!a.type||a.type.toUpperCase()=="GET")&&(a.url=appendQuery(a.url,a.data))}function serialize(a,b,c,d){var e=$.isArray(b);$.each(b,function(b,f){d&&(b=c?d:d+"["+(e?"":b)+"]"),!d&&e?a.add(f.name,f.value):(c?$.isArray(f):isObject(f))?serialize(a,f,c,b):a.add(b,f)})}var jsonpID=0,isObject=$.isObject,document=window.document,key,name,rscript=/)<[^<]*)*<\/script>/gi,scriptTypeRE=/^(?:text|application)\/javascript/i,xmlTypeRE=/^(?:text|application)\/xml/i,jsonType="application/json",htmlType="text/html",blankRE=/^\s*$/;$.active=0,$.ajaxJSONP=function(a){var b="jsonp"+ ++jsonpID,c=document.createElement("script"),d=function(){$(c).remove(),b in window&&(window[b]=empty),ajaxComplete("abort",e,a)},e={abort:d},f;return a.error&&(c.onerror=function(){e.abort(),a.error()}),window[b]=function(d){clearTimeout(f),$(c).remove(),delete window[b],ajaxSuccess(d,e,a)},serializeData(a),c.src=a.url.replace(/=\?/,"="+b),$("head").append(c),a.timeout>0&&(f=setTimeout(function(){e.abort(),ajaxComplete("timeout",e,a)},a.timeout)),e},$.ajaxSettings={type:"GET",beforeSend:empty,success:empty,error:empty,complete:empty,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript",json:jsonType,xml:"application/xml, text/xml",html:htmlType,text:"text/plain"},crossDomain:!1,timeout:0},$.ajax=function(options){var settings=$.extend({},options||{});for(key in $.ajaxSettings)settings[key]===undefined&&(settings[key]=$.ajaxSettings[key]);ajaxStart(settings),settings.crossDomain||(settings.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(settings.url)&&RegExp.$2!=window.location.host);var dataType=settings.dataType,hasPlaceholder=/=\?/.test(settings.url);if(dataType=="jsonp"||hasPlaceholder)return hasPlaceholder||(settings.url=appendQuery(settings.url,"callback=?")),$.ajaxJSONP(settings);settings.url||(settings.url=window.location.toString()),serializeData(settings);var mime=settings.accepts[dataType],baseHeaders={},protocol=/^([\w-]+:)\/\//.test(settings.url)?RegExp.$1:window.location.protocol,xhr=$.ajaxSettings.xhr(),abortTimeout;settings.crossDomain||(baseHeaders["X-Requested-With"]="XMLHttpRequest"),mime&&(baseHeaders.Accept=mime,mime.indexOf(",")>-1&&(mime=mime.split(",",2)[0]),xhr.overrideMimeType&&xhr.overrideMimeType(mime));if(settings.contentType||settings.data&&settings.type.toUpperCase()!="GET")baseHeaders["Content-Type"]=settings.contentType||"application/x-www-form-urlencoded";settings.headers=$.extend(baseHeaders,settings.headers||{}),xhr.onreadystatechange=function(){if(xhr.readyState==4){clearTimeout(abortTimeout);var result,error=!1;if(xhr.status>=200&&xhr.status<300||xhr.status==304||xhr.status==0&&protocol=="file:"){dataType=dataType||mimeToDataType(xhr.getResponseHeader("content-type")),result=xhr.responseText;try{dataType=="script"?(1,eval)(result):dataType=="xml"?result=xhr.responseXML:dataType=="json"&&(result=blankRE.test(result)?null:JSON.parse(result))}catch(e){error=e}error?ajaxError(error,"parsererror",xhr,settings):ajaxSuccess(result,xhr,settings)}else ajaxError(null,"error",xhr,settings)}};var async="async"in settings?settings.async:!0;xhr.open(settings.type,settings.url,async);for(name in settings.headers)xhr.setRequestHeader(name,settings.headers[name]);return ajaxBeforeSend(xhr,settings)===!1?(xhr.abort(),!1):(settings.timeout>0&&(abortTimeout=setTimeout(function(){xhr.onreadystatechange=empty,xhr.abort(),ajaxError(null,"timeout",xhr,settings)},settings.timeout)),xhr.send(settings.data?settings.data:null),xhr)},$.get=function(a,b){return $.ajax({url:a,success:b})},$.post=function(a,b,c,d){return $.isFunction(b)&&(d=d||c,c=b,b=null),$.ajax({type:"POST",url:a,data:b,success:c,dataType:d})},$.getJSON=function(a,b){return $.ajax({url:a,success:b,dataType:"json"})},$.fn.load=function(a,b){if(!this.length)return this;var c=this,d=a.split(/\s/),e;return d.length>1&&(a=d[0],e=d[1]),$.get(a,function(a){c.html(e?$(document.createElement("div")).html(a.replace(rscript,"")).find(e).html():a),b&&b.call(c)}),this};var escape=encodeURIComponent;$.param=function(a,b){var c=[];return c.add=function(a,b){this.push(escape(a)+"="+escape(b))},serialize(c,a,b),c.join("&").replace("%20","+")}}(Zepto),function(a){a.fn.serializeArray=function(){var b=[],c;return a(Array.prototype.slice.call(this.get(0).elements)).each(function(){c=a(this);var d=c.attr("type");this.nodeName.toLowerCase()!="fieldset"&&!this.disabled&&d!="submit"&&d!="reset"&&d!="button"&&(d!="radio"&&d!="checkbox"||this.checked)&&b.push({name:c.attr("name"),value:c.val()})}),b},a.fn.serialize=function(){var a=[];return this.serializeArray().forEach(function(b){a.push(encodeURIComponent(b.name)+"="+encodeURIComponent(b.value))}),a.join("&")},a.fn.submit=function(b){if(b)this.bind("submit",b);else if(this.length){var c=a.Event("submit");this.eq(0).trigger(c),c.defaultPrevented||this.get(0).submit()}return this}}(Zepto),function(a){function d(a){return"tagName"in a?a:a.parentNode}function e(a,b,c,d){var e=Math.abs(a-b),f=Math.abs(c-d);return e>=f?a-b>0?"Left":"Right":c-d>0?"Up":"Down"}function h(){g=null,b.last&&(b.el.trigger("longTap"),b={})}function i(){g&&clearTimeout(g),g=null}var b={},c,f=750,g;a(document).ready(function(){var j,k;a(document.body).bind("touchstart",function(e){j=Date.now(),k=j-(b.last||j),b.el=a(d(e.touches[0].target)),c&&clearTimeout(c),b.x1=e.touches[0].pageX,b.y1=e.touches[0].pageY,k>0&&k<=250&&(b.isDoubleTap=!0),b.last=j,g=setTimeout(h,f)}).bind("touchmove",function(a){i(),b.x2=a.touches[0].pageX,b.y2=a.touches[0].pageY}).bind("touchend",function(a){i(),b.isDoubleTap?(b.el.trigger("doubleTap"),b={}):b.x2&&Math.abs(b.x1-b.x2)>30||b.y2&&Math.abs(b.y1-b.y2)>30?(b.el.trigger("swipe")&&b.el.trigger("swipe"+e(b.x1,b.x2,b.y1,b.y2)),b={}):"last"in b&&(b.el.trigger("tap"),c=setTimeout(function(){c=null,b.el.trigger("singleTap"),b={}},250))}).bind("touchcancel",function(){c&&clearTimeout(c),g&&clearTimeout(g),g=c=null,b={}})}),["swipe","swipeLeft","swipeRight","swipeUp","swipeDown","doubleTap","tap","singleTap","longTap"].forEach(function(b){a.fn[b]=function(a){return this.bind(b,a)}})}(Zepto); --------------------------------------------------------------------------------