├── 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=/