├── control
└── Acore.js
├── image
├── 1.jpg
└── loading_img_56@2x.png
├── jssdk
├── php
│ ├── access_token.php
│ ├── jsapi_ticket.php
│ ├── jssdk.php
│ └── sample.php
└── readme.txt
├── README.md
├── dist
├── fonts
│ ├── ionicons.eot
│ ├── ionicons.ttf
│ └── ionicons.woff
├── css
│ └── base.css
└── js
│ ├── jweixin-1.0.0.js
│ ├── jweixinConfig.js
│ └── angular-route.js
├── .settings
└── org.eclipse.core.resources.prefs
├── register.php
├── view
├── map.html
├── setting.html
├── gameDetail.html
├── login.html
├── register.html
├── game.html
└── index.html
├── .project
├── baiduApi.php
├── rebot.php
├── login.php
├── baiduApi2.php
├── baiduApi3.php
├── ghpage
├── tab.html
└── touch.html
├── weixinjssdk.html
├── wx_sample.php
├── index.html
└── index.php
/control/Acore.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/image/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wscats/weixin-demo/master/image/1.jpg
--------------------------------------------------------------------------------
/jssdk/php/access_token.php:
--------------------------------------------------------------------------------
1 |
2 | {"access_token":"","expire_time":0}
3 |
--------------------------------------------------------------------------------
/jssdk/php/jsapi_ticket.php:
--------------------------------------------------------------------------------
1 |
2 | {"jsapi_ticket":"","expire_time":0}
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 日日新闻客户端
2 |
3 | 效果如下
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/dist/fonts/ionicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wscats/weixin-demo/master/dist/fonts/ionicons.eot
--------------------------------------------------------------------------------
/dist/fonts/ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wscats/weixin-demo/master/dist/fonts/ionicons.ttf
--------------------------------------------------------------------------------
/dist/fonts/ionicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wscats/weixin-demo/master/dist/fonts/ionicons.woff
--------------------------------------------------------------------------------
/image/loading_img_56@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wscats/weixin-demo/master/image/loading_img_56@2x.png
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//dist/js/jweixinConfig.js=UTF-8
3 |
--------------------------------------------------------------------------------
/register.php:
--------------------------------------------------------------------------------
1 | 'abcdefghijk','userId'=>'1',"xss"=>"/>
--------------------------------------------------------------------------------
/view/map.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | weixin
4 |
5 |
6 |
7 |
8 |
9 | com.aptana.ide.core.unifiedBuilder
10 |
11 |
12 |
13 |
14 |
15 | com.aptana.projects.webnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/jssdk/readme.txt:
--------------------------------------------------------------------------------
1 | JAVA, Node, Python 部分代码只实现了签名算法,需要开发者传入 jsapi_ticket 和 url ,其中 jsapi_ticket 需要通过 http://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=ACCESS_TOKEN 接口获取,url 为调用页面的完整 url 。
2 |
3 | PHP 部分代码包括了获取 access_token 和 jsapi_ticket 的操作,只需传入 appid 和 appsecret 即可,但要注意如果已有其他业务需要使用 access_token 的话,应修改获取 access_token 部分代码从全局缓存中获取,防止重复获取 access_token ,超过调用频率。
4 |
5 | 注意事项:
6 | 1. jsapi_ticket 的有效期为 7200 秒,开发者必须全局缓存 jsapi_ticket ,防止超过调用频率。
7 |
--------------------------------------------------------------------------------
/baiduApi.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/view/setting.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/dist/css/base.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | header {
7 | background-color: #d43d3d;
8 | position: fixed;
9 | z-index: 10;
10 | width: 100%;
11 | text-align: center;
12 | color: white;
13 | height: 50px;
14 | line-height: 50px;
15 | top: 0px;
16 | }
17 |
18 | aside {
19 | position: fixed;
20 | top: 50px;
21 | width: 100%;
22 | z-index: 9;
23 | padding: 10px;
24 | background: #f4f5f6;
25 | }
26 |
27 | aside p {
28 | /*padding: 10px;*/
29 | }
30 |
31 | aside p a{
32 | text-decoration: none;
33 | color: black;
34 | }
35 |
36 | .isNavRed{
37 | color: red;
38 | }
39 |
--------------------------------------------------------------------------------
/rebot.php:
--------------------------------------------------------------------------------
1 | text;
22 | ?>
--------------------------------------------------------------------------------
/login.php:
--------------------------------------------------------------------------------
1 | 'abcdefghijk','userId'=>'1',"xss"=>"".""/> 'abcdefghijk','userId'=>'1','infomation'=>array()));
17 | //var_dump(json_decode(array('token'=>'abcdefghijk')));
18 | }else{
19 | echo "登陸失敗";
20 | }*/
21 | ?>
--------------------------------------------------------------------------------
/baiduApi2.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/view/gameDetail.html:
--------------------------------------------------------------------------------
1 |
2 | {{new.title}}
3 |
4 |
5 |
6 |
7 | {{new.desc}}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/view/login.html:
--------------------------------------------------------------------------------
1 |
2 | 信息登錄系統
3 |
4 |
5 | 登錄
6 | 註冊
7 |
8 |
--------------------------------------------------------------------------------
/view/register.html:
--------------------------------------------------------------------------------
1 |
2 | 信息註冊系統
3 |
4 |
5 | 註冊
6 | 登錄
7 |
8 |
--------------------------------------------------------------------------------
/baiduApi3.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ghpage/tab.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Angular选项卡
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
21 |
37 |
53 |
54 |
--------------------------------------------------------------------------------
/weixinjssdk.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
57 |
58 |
--------------------------------------------------------------------------------
/view/game.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/view/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/wx_sample.php:
--------------------------------------------------------------------------------
1 | valid();
11 | } else {
12 | $wechatObj -> responseMsg();
13 | }
14 |
15 | class wechatCallbackapiTest {
16 | public function valid() {
17 | $echoStr = $_GET["echostr"];
18 |
19 | //valid signature , option
20 | if ($this -> checkSignature()) {
21 | echo $echoStr;
22 | exit ;
23 | }
24 | }
25 |
26 | public function responseMsg() {
27 | //get post data, May be due to the different environments
28 | $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
29 |
30 | //extract post data
31 | if (!empty($postStr)) {
32 | /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
33 | the best way is to check the validity of xml by yourself */
34 | libxml_disable_entity_loader(true);
35 | $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
36 | $fromUsername = $postObj -> FromUserName;
37 | $toUsername = $postObj -> ToUserName;
38 | $keyword = trim($postObj -> Content);
39 | $time = time();
40 | $textTpl = "
41 |
42 |
43 | %s
44 |
45 |
46 | 0
47 | ";
48 |
49 | if (!empty($keyword)) {
50 | $apiKey = "c75ba576f50ddaa5fd2a87615d144ecf";
51 | $apiURL = "http://www.tuling123.com/openapi/api?key=KEY&info=INFO";
52 | $reqInfo = $keyword;
53 | $url = str_replace("INFO", $reqInfo, str_replace("KEY", $apiKey, $apiURL));
54 | $res = file_get_contents($url);
55 | $resObj = json_decode($res);
56 | $contentStr = $resObj->text;
57 | $msgType = "text";
58 | //$contentStr = "Welcome to wechat world! I'am wsscat,nice to meet you";
59 | $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
60 | echo $resultStr;
61 | } else {
62 | echo "Input something...";
63 | }
64 |
65 | } else {
66 | echo "";
67 | exit ;
68 | }
69 | }
70 |
71 | private function checkSignature() {
72 | // you must define TOKEN by yourself
73 | if (!defined("TOKEN")) {
74 | throw new Exception('TOKEN is not defined!');
75 | }
76 |
77 | $signature = $_GET["signature"];
78 | $timestamp = $_GET["timestamp"];
79 | $nonce = $_GET["nonce"];
80 |
81 | $token = TOKEN;
82 | $tmpArr = array($token, $timestamp, $nonce);
83 | // use SORT_STRING rule
84 | sort($tmpArr, SORT_STRING);
85 | $tmpStr = implode($tmpArr);
86 | $tmpStr = sha1($tmpStr);
87 |
88 | if ($tmpStr == $signature) {
89 | return true;
90 | } else {
91 | return false;
92 | }
93 | }
94 |
95 | }
96 | ?>
--------------------------------------------------------------------------------
/ghpage/touch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Javascript事件监听
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
32 |
105 |
106 |
--------------------------------------------------------------------------------
/jssdk/php/jssdk.php:
--------------------------------------------------------------------------------
1 | appId = $appId;
8 | $this->appSecret = $appSecret;
9 | }
10 |
11 | public function getSignPackage() {
12 | $jsapiTicket = $this->getJsApiTicket();
13 |
14 | // 注意 URL 一定要动态获取,不能 hardcode.
15 | $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
16 | $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
17 |
18 | $timestamp = time();
19 | $nonceStr = $this->createNonceStr();
20 |
21 | // 这里参数的顺序要按照 key 值 ASCII 码升序排序
22 | $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
23 |
24 | $signature = sha1($string);
25 |
26 | $signPackage = array(
27 | "appId" => $this->appId,
28 | "nonceStr" => $nonceStr,
29 | "timestamp" => $timestamp,
30 | "url" => $url,
31 | "signature" => $signature,
32 | "rawString" => $string
33 | );
34 | return $signPackage;
35 | }
36 |
37 | private function createNonceStr($length = 16) {
38 | $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
39 | $str = "";
40 | for ($i = 0; $i < $length; $i++) {
41 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
42 | }
43 | return $str;
44 | }
45 |
46 | private function getJsApiTicket() {
47 | // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
48 | $data = json_decode($this->get_php_file("jsapi_ticket.php"));
49 | if ($data->expire_time < time()) {
50 | $accessToken = $this->getAccessToken();
51 | // 如果是企业号用以下 URL 获取 ticket
52 | // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken";
53 | $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
54 | $res = json_decode($this->httpGet($url));
55 | $ticket = $res->ticket;
56 | if ($ticket) {
57 | $data->expire_time = time() + 7000;
58 | $data->jsapi_ticket = $ticket;
59 | $this->set_php_file("http://wsscatwexin-jsssdk.stor.sinaapp.com/jsapi_ticket.php", json_encode($data));
60 | }
61 | } else {
62 | $ticket = $data->jsapi_ticket;
63 | }
64 |
65 | return $ticket;
66 | }
67 |
68 | private function getAccessToken() {
69 | // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
70 | $data = json_decode($this->get_php_file("access_token.php"));
71 | if ($data->expire_time < time()) {
72 | // 如果是企业号用以下URL获取access_token
73 | // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
74 | $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
75 | $res = json_decode($this->httpGet($url));
76 | $access_token = $res->access_token;
77 | if ($access_token) {
78 | $data->expire_time = time() + 7000;
79 | $data->access_token = $access_token;
80 | $this->set_php_file("http://wsscatwexin-jsssdk.stor.sinaapp.com/access_token.php", json_encode($data));
81 | }
82 | } else {
83 | $access_token = $data->access_token;
84 | }
85 | return $access_token;
86 | }
87 |
88 | private function httpGet($url) {
89 | $curl = curl_init();
90 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
91 | curl_setopt($curl, CURLOPT_TIMEOUT, 500);
92 | // 为保证第三方服务器与微信服务器之间数据传输的安全性,所有微信接口采用https方式调用,必须使用下面2行代码打开ssl安全校验。
93 | // 如果在部署过程中代码在此处验证失败,请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。
94 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
95 | curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true);
96 | curl_setopt($curl, CURLOPT_URL, $url);
97 |
98 | $res = curl_exec($curl);
99 | curl_close($curl);
100 |
101 | return $res;
102 | }
103 |
104 | private function get_php_file($filename) {
105 | return trim(substr(file_get_contents($filename), 15));
106 | }
107 | private function set_php_file($filename, $content) {
108 | $fp = fopen($filename, "w");
109 | fwrite($fp, "" . $content);
110 | fclose($fp);
111 | }
112 | }
113 |
114 |
--------------------------------------------------------------------------------
/dist/js/jweixin-1.0.0.js:
--------------------------------------------------------------------------------
1 | !function(a,b){"function"==typeof define&&(define.amd||define.cmd)?define(function(){return b(a)}):b(a,!0)}(this,function(a,b){function c(b,c,d){a.WeixinJSBridge?WeixinJSBridge.invoke(b,e(c),function(a){g(b,a,d)}):j(b,d)}function d(b,c,d){a.WeixinJSBridge?WeixinJSBridge.on(b,function(a){d&&d.trigger&&d.trigger(a),g(b,a,c)}):d?j(b,d):j(b,c)}function e(a){return a=a||{},a.appId=E.appId,a.verifyAppId=E.appId,a.verifySignType="sha1",a.verifyTimestamp=E.timestamp+"",a.verifyNonceStr=E.nonceStr,a.verifySignature=E.signature,a}function f(a){return{timeStamp:a.timestamp+"",nonceStr:a.nonceStr,"package":a.package,paySign:a.paySign,signType:a.signType||"SHA1"}}function g(a,b,c){var d,e,f;switch(delete b.err_code,delete b.err_desc,delete b.err_detail,d=b.errMsg,d||(d=b.err_msg,delete b.err_msg,d=h(a,d),b.errMsg=d),c=c||{},c._complete&&(c._complete(b),delete c._complete),d=b.errMsg||"",E.debug&&!c.isInnerInvoke&&alert(JSON.stringify(b)),e=d.indexOf(":"),f=d.substring(e+1)){case"ok":c.success&&c.success(b);break;case"cancel":c.cancel&&c.cancel(b);break;default:c.fail&&c.fail(b)}c.complete&&c.complete(b)}function h(a,b){var e,f,c=a,d=p[c];return d&&(c=d),e="ok",b&&(f=b.indexOf(":"),e=b.substring(f+1),"confirm"==e&&(e="ok"),"failed"==e&&(e="fail"),-1!=e.indexOf("failed_")&&(e=e.substring(7)),-1!=e.indexOf("fail_")&&(e=e.substring(5)),e=e.replace(/_/g," "),e=e.toLowerCase(),("access denied"==e||"no permission to execute"==e)&&(e="permission denied"),"config"==c&&"function not exist"==e&&(e="ok"),""==e&&(e="fail")),b=c+":"+e}function i(a){var b,c,d,e;if(a){for(b=0,c=a.length;c>b;++b)d=a[b],e=o[d],e&&(a[b]=e);return a}}function j(a,b){if(!(!E.debug||b&&b.isInnerInvoke)){var c=p[a];c&&(a=c),b&&b._complete&&delete b._complete,console.log('"'+a+'",',b||"")}}function k(){0!=D.preVerifyState&&(u||v||E.debug||"6.0.2">z||D.systemType<0||A||(A=!0,D.appId=E.appId,D.initTime=C.initEndTime-C.initStartTime,D.preVerifyTime=C.preVerifyEndTime-C.preVerifyStartTime,H.getNetworkType({isInnerInvoke:!0,success:function(a){var b,c;D.networkType=a.networkType,b="http://open.weixin.qq.com/sdk/report?v="+D.version+"&o="+D.preVerifyState+"&s="+D.systemType+"&c="+D.clientVersion+"&a="+D.appId+"&n="+D.networkType+"&i="+D.initTime+"&p="+D.preVerifyTime+"&u="+D.url,c=new Image,c.src=b}})))}function l(){return(new Date).getTime()}function m(b){w&&(a.WeixinJSBridge?b():q.addEventListener&&q.addEventListener("WeixinJSBridgeReady",b,!1))}function n(){H.invoke||(H.invoke=function(b,c,d){a.WeixinJSBridge&&WeixinJSBridge.invoke(b,e(c),d)},H.on=function(b,c){a.WeixinJSBridge&&WeixinJSBridge.on(b,c)})}var o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H;if(!a.jWeixin)return o={config:"preVerifyJSAPI",onMenuShareTimeline:"menu:share:timeline",onMenuShareAppMessage:"menu:share:appmessage",onMenuShareQQ:"menu:share:qq",onMenuShareWeibo:"menu:share:weiboApp",onMenuShareQZone:"menu:share:QZone",previewImage:"imagePreview",getLocation:"geoLocation",openProductSpecificView:"openProductViewWithPid",addCard:"batchAddCard",openCard:"batchViewCard",chooseWXPay:"getBrandWCPayRequest"},p=function(){var b,a={};for(b in o)a[o[b]]=b;return a}(),q=a.document,r=q.title,s=navigator.userAgent.toLowerCase(),t=navigator.platform.toLowerCase(),u=!(!t.match("mac")&&!t.match("win")),v=-1!=s.indexOf("wxdebugger"),w=-1!=s.indexOf("micromessenger"),x=-1!=s.indexOf("android"),y=-1!=s.indexOf("iphone")||-1!=s.indexOf("ipad"),z=function(){var a=s.match(/micromessenger\/(\d+\.\d+\.\d+)/)||s.match(/micromessenger\/(\d+\.\d+)/);return a?a[1]:""}(),A=!1,B=!1,C={initStartTime:l(),initEndTime:0,preVerifyStartTime:0,preVerifyEndTime:0},D={version:1,appId:"",initTime:0,preVerifyTime:0,networkType:"",preVerifyState:1,systemType:y?1:x?2:-1,clientVersion:z,url:encodeURIComponent(location.href)},E={},F={_completes:[]},G={state:0,data:{}},m(function(){C.initEndTime=l()}),H={config:function(a){E=a,j("config",a);var b=E.check===!1?!1:!0;m(function(){var a,d,e;if(b)c(o.config,{verifyJsApiList:i(E.jsApiList)},function(){F._complete=function(a){C.preVerifyEndTime=l(),G.state=1,G.data=a},F.success=function(){D.preVerifyState=0},F.fail=function(a){F._fail?F._fail(a):G.state=-1};var a=F._completes;return a.push(function(){k()}),F.complete=function(){for(var c=0,d=a.length;d>c;++c)a[c]();F._completes=[]},F}()),C.preVerifyStartTime=l();else{for(G.state=1,a=F._completes,d=0,e=a.length;e>d;++d)a[d]();F._completes=[]}}),E.beta&&n()},ready:function(a){0!=G.state?a():(F._completes.push(a),!w&&E.debug&&a())},error:function(a){"6.0.2">z||B||(B=!0,-1==G.state?a(G.data):F._fail=a)},checkJsApi:function(a){var b=function(a){var c,d,b=a.checkResult;for(c in b)d=p[c],d&&(b[d]=b[c],delete b[c]);return a};c("checkJsApi",{jsApiList:i(a.jsApiList)},function(){return a._complete=function(a){if(x){var c=a.checkResult;c&&(a.checkResult=JSON.parse(c))}a=b(a)},a}())},onMenuShareTimeline:function(a){d(o.onMenuShareTimeline,{complete:function(){c("shareTimeline",{title:a.title||r,desc:a.title||r,img_url:a.imgUrl||"",link:a.link||location.href,type:a.type||"link",data_url:a.dataUrl||""},a)}},a)},onMenuShareAppMessage:function(a){d(o.onMenuShareAppMessage,{complete:function(){c("sendAppMessage",{title:a.title||r,desc:a.desc||"",link:a.link||location.href,img_url:a.imgUrl||"",type:a.type||"link",data_url:a.dataUrl||""},a)}},a)},onMenuShareQQ:function(a){d(o.onMenuShareQQ,{complete:function(){c("shareQQ",{title:a.title||r,desc:a.desc||"",img_url:a.imgUrl||"",link:a.link||location.href},a)}},a)},onMenuShareWeibo:function(a){d(o.onMenuShareWeibo,{complete:function(){c("shareWeiboApp",{title:a.title||r,desc:a.desc||"",img_url:a.imgUrl||"",link:a.link||location.href},a)}},a)},onMenuShareQZone:function(a){d(o.onMenuShareQZone,{complete:function(){c("shareQZone",{title:a.title||r,desc:a.desc||"",img_url:a.imgUrl||"",link:a.link||location.href},a)}},a)},startRecord:function(a){c("startRecord",{},a)},stopRecord:function(a){c("stopRecord",{},a)},onVoiceRecordEnd:function(a){d("onVoiceRecordEnd",a)},playVoice:function(a){c("playVoice",{localId:a.localId},a)},pauseVoice:function(a){c("pauseVoice",{localId:a.localId},a)},stopVoice:function(a){c("stopVoice",{localId:a.localId},a)},onVoicePlayEnd:function(a){d("onVoicePlayEnd",a)},uploadVoice:function(a){c("uploadVoice",{localId:a.localId,isShowProgressTips:0==a.isShowProgressTips?0:1},a)},downloadVoice:function(a){c("downloadVoice",{serverId:a.serverId,isShowProgressTips:0==a.isShowProgressTips?0:1},a)},translateVoice:function(a){c("translateVoice",{localId:a.localId,isShowProgressTips:0==a.isShowProgressTips?0:1},a)},chooseImage:function(a){c("chooseImage",{scene:"1|2",count:a.count||9,sizeType:a.sizeType||["original","compressed"],sourceType:a.sourceType||["album","camera"]},function(){return a._complete=function(a){if(x){var b=a.localIds;b&&(a.localIds=JSON.parse(b))}},a}())},previewImage:function(a){c(o.previewImage,{current:a.current,urls:a.urls},a)},uploadImage:function(a){c("uploadImage",{localId:a.localId,isShowProgressTips:0==a.isShowProgressTips?0:1},a)},downloadImage:function(a){c("downloadImage",{serverId:a.serverId,isShowProgressTips:0==a.isShowProgressTips?0:1},a)},getNetworkType:function(a){var b=function(a){var c,d,e,b=a.errMsg;if(a.errMsg="getNetworkType:ok",c=a.subtype,delete a.subtype,c)a.networkType=c;else switch(d=b.indexOf(":"),e=b.substring(d+1)){case"wifi":case"edge":case"wwan":a.networkType=e;break;default:a.errMsg="getNetworkType:fail"}return a};c("getNetworkType",{},function(){return a._complete=function(a){a=b(a)},a}())},openLocation:function(a){c("openLocation",{latitude:a.latitude,longitude:a.longitude,name:a.name||"",address:a.address||"",scale:a.scale||28,infoUrl:a.infoUrl||""},a)},getLocation:function(a){a=a||{},c(o.getLocation,{type:a.type||"wgs84"},function(){return a._complete=function(a){delete a.type},a}())},hideOptionMenu:function(a){c("hideOptionMenu",{},a)},showOptionMenu:function(a){c("showOptionMenu",{},a)},closeWindow:function(a){a=a||{},c("closeWindow",{},a)},hideMenuItems:function(a){c("hideMenuItems",{menuList:a.menuList},a)},showMenuItems:function(a){c("showMenuItems",{menuList:a.menuList},a)},hideAllNonBaseMenuItem:function(a){c("hideAllNonBaseMenuItem",{},a)},showAllNonBaseMenuItem:function(a){c("showAllNonBaseMenuItem",{},a)},scanQRCode:function(a){a=a||{},c("scanQRCode",{needResult:a.needResult||0,scanType:a.scanType||["qrCode","barCode"]},function(){return a._complete=function(a){var b,c;y&&(b=a.resultStr,b&&(c=JSON.parse(b),a.resultStr=c&&c.scan_code&&c.scan_code.scan_result))},a}())},openProductSpecificView:function(a){c(o.openProductSpecificView,{pid:a.productId,view_type:a.viewType||0,ext_info:a.extInfo},a)},addCard:function(a){var e,f,g,h,b=a.cardList,d=[];for(e=0,f=b.length;f>e;++e)g=b[e],h={card_id:g.cardId,card_ext:g.cardExt},d.push(h);c(o.addCard,{card_list:d},function(){return a._complete=function(a){var c,d,e,b=a.card_list;if(b){for(b=JSON.parse(b),c=0,d=b.length;d>c;++c)e=b[c],e.cardId=e.card_id,e.cardExt=e.card_ext,e.isSuccess=e.is_succ?!0:!1,delete e.card_id,delete e.card_ext,delete e.is_succ;a.cardList=b,delete a.card_list}},a}())},chooseCard:function(a){c("chooseCard",{app_id:E.appId,location_id:a.shopId||"",sign_type:a.signType||"SHA1",card_id:a.cardId||"",card_type:a.cardType||"",card_sign:a.cardSign,time_stamp:a.timestamp+"",nonce_str:a.nonceStr},function(){return a._complete=function(a){a.cardList=a.choose_card_info,delete a.choose_card_info},a}())},openCard:function(a){var e,f,g,h,b=a.cardList,d=[];for(e=0,f=b.length;f>e;++e)g=b[e],h={card_id:g.cardId,code:g.code},d.push(h);c(o.openCard,{card_list:d},a)},chooseWXPay:function(a){c(o.chooseWXPay,f(a),a)}},b&&(a.wx=a.jWeixin=H),H});
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
30 |
31 |
32 |
33 |
396 |
397 |
398 |
399 |
400 |
401 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | valid();
10 |
11 | class wechatCallbackapiTest
12 | {
13 | public function valid()
14 | {
15 | $echoStr = $_GET["echostr"];
16 |
17 | //valid signature , option
18 | if($this->checkSignature()){
19 | echo $echoStr;
20 | exit;
21 | }
22 | }
23 |
24 | public function responseMsg()
25 | {
26 | //get post data, May be due to the different environments
27 | $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
28 |
29 | //extract post data
30 | if (!empty($postStr)){
31 | /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
32 | the best way is to check the validity of xml by yourself */
33 | libxml_disable_entity_loader(true);
34 | $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
35 | $fromUsername = $postObj->FromUserName;
36 | $toUsername = $postObj->ToUserName;
37 | $keyword = trim($postObj->Content);
38 | $time = time();
39 | $textTpl = "
40 |
41 |
42 | %s
43 |
44 |
45 | 0
46 | ";
47 | if(!empty( $keyword ))
48 | {
49 | $msgType = "text";
50 | $contentStr = "Welcome to wechat world!";
51 | $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
52 | echo $resultStr;
53 | }else{
54 | echo "Input something...";
55 | }
56 |
57 | }else {
58 | echo "";
59 | exit;
60 | }
61 | }
62 |
63 | private function checkSignature()
64 | {
65 | // you must define TOKEN by yourself
66 | if (!defined("TOKEN")) {
67 | throw new Exception('TOKEN is not defined!');
68 | }
69 |
70 | $signature = $_GET["signature"];
71 | $timestamp = $_GET["timestamp"];
72 | $nonce = $_GET["nonce"];
73 |
74 | $token = TOKEN;
75 | $tmpArr = array($token, $timestamp, $nonce);
76 | // use SORT_STRING rule
77 | sort($tmpArr, SORT_STRING);
78 | $tmpStr = implode( $tmpArr );
79 | $tmpStr = sha1( $tmpStr );
80 |
81 | if( $tmpStr == $signature ){
82 | return true;
83 | }else{
84 | return false;
85 | }
86 | }
87 | }
88 | ?>
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
112 |
115 |
116 |
117 |
118 |
481 |
482 |
483 |
484 |
485 |
486 |
--------------------------------------------------------------------------------
/dist/js/jweixinConfig.js:
--------------------------------------------------------------------------------
1 | /*
2 | * 注意:
3 | * 1. 所有的JS接口只能在公众号绑定的域名下调用,公众号开发者需要先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
4 | * 2. 如果发现在 Android 不能分享自定义内容,请到官网下载最新的包覆盖安装,Android 自定义分享接口需升级至 6.0.2.58 版本及以上。
5 | * 3. 完整 JS-SDK 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
6 | *
7 | * 如有问题请通过以下渠道反馈:
8 | * 邮箱地址:weixin-open@qq.com
9 | * 邮件主题:【微信JS-SDK反馈】具体问题
10 | * 邮件内容说明:用简明的语言描述问题所在,并交代清楚遇到该问题的场景,可附上截屏图片,微信团队会尽快处理你的反馈。
11 | */
12 | wx.ready(function () {
13 | // 1 判断当前版本是否支持指定 JS 接口,支持批量判断
14 | document.querySelector('#checkJsApi').onclick = function () {
15 | wx.checkJsApi({
16 | jsApiList: [
17 | 'getNetworkType',
18 | 'previewImage'
19 | ],
20 | success: function (res) {
21 | alert(JSON.stringify(res));
22 | }
23 | });
24 | };
25 |
26 | // 2. 分享接口
27 | // 2.1 监听“分享给朋友”,按钮点击、自定义分享内容及分享结果接口
28 | document.querySelector('#onMenuShareAppMessage').onclick = function () {
29 | wx.onMenuShareAppMessage({
30 | title: '互联网之子',
31 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。',
32 | link: 'http://movie.douban.com/subject/25785114/',
33 | imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg',
34 | trigger: function (res) {
35 | // 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
36 | alert('用户点击发送给朋友');
37 | },
38 | success: function (res) {
39 | alert('已分享');
40 | },
41 | cancel: function (res) {
42 | alert('已取消');
43 | },
44 | fail: function (res) {
45 | alert(JSON.stringify(res));
46 | }
47 | });
48 | alert('已注册获取“发送给朋友”状态事件');
49 | };
50 |
51 | // 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
52 | document.querySelector('#onMenuShareTimeline').onclick = function () {
53 | wx.onMenuShareTimeline({
54 | title: '互联网之子',
55 | link: 'http://movie.douban.com/subject/25785114/',
56 | imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg',
57 | trigger: function (res) {
58 | // 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
59 | alert('用户点击分享到朋友圈');
60 | },
61 | success: function (res) {
62 | alert('已分享');
63 | },
64 | cancel: function (res) {
65 | alert('已取消');
66 | },
67 | fail: function (res) {
68 | alert(JSON.stringify(res));
69 | }
70 | });
71 | alert('已注册获取“分享到朋友圈”状态事件');
72 | };
73 |
74 | // 2.3 监听“分享到QQ”按钮点击、自定义分享内容及分享结果接口
75 | document.querySelector('#onMenuShareQQ').onclick = function () {
76 | wx.onMenuShareQQ({
77 | title: '互联网之子',
78 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。',
79 | link: 'http://movie.douban.com/subject/25785114/',
80 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg',
81 | trigger: function (res) {
82 | alert('用户点击分享到QQ');
83 | },
84 | complete: function (res) {
85 | alert(JSON.stringify(res));
86 | },
87 | success: function (res) {
88 | alert('已分享');
89 | },
90 | cancel: function (res) {
91 | alert('已取消');
92 | },
93 | fail: function (res) {
94 | alert(JSON.stringify(res));
95 | }
96 | });
97 | alert('已注册获取“分享到 QQ”状态事件');
98 | };
99 |
100 | // 2.4 监听“分享到微博”按钮点击、自定义分享内容及分享结果接口
101 | document.querySelector('#onMenuShareWeibo').onclick = function () {
102 | wx.onMenuShareWeibo({
103 | title: '互联网之子',
104 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。',
105 | link: 'http://movie.douban.com/subject/25785114/',
106 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg',
107 | trigger: function (res) {
108 | alert('用户点击分享到微博');
109 | },
110 | complete: function (res) {
111 | alert(JSON.stringify(res));
112 | },
113 | success: function (res) {
114 | alert('已分享');
115 | },
116 | cancel: function (res) {
117 | alert('已取消');
118 | },
119 | fail: function (res) {
120 | alert(JSON.stringify(res));
121 | }
122 | });
123 | alert('已注册获取“分享到微博”状态事件');
124 | };
125 |
126 | // 2.5 监听“分享到QZone”按钮点击、自定义分享内容及分享接口
127 | document.querySelector('#onMenuShareQZone').onclick = function () {
128 | wx.onMenuShareQZone({
129 | title: '互联网之子',
130 | desc: '在长大的过程中,我才慢慢发现,我身边的所有事,别人跟我说的所有事,那些所谓本来如此,注定如此的事,它们其实没有非得如此,事情是可以改变的。更重要的是,有些事既然错了,那就该做出改变。',
131 | link: 'http://movie.douban.com/subject/25785114/',
132 | imgUrl: 'http://img3.douban.com/view/movie_poster_cover/spst/public/p2166127561.jpg',
133 | trigger: function (res) {
134 | alert('用户点击分享到QZone');
135 | },
136 | complete: function (res) {
137 | alert(JSON.stringify(res));
138 | },
139 | success: function (res) {
140 | alert('已分享');
141 | },
142 | cancel: function (res) {
143 | alert('已取消');
144 | },
145 | fail: function (res) {
146 | alert(JSON.stringify(res));
147 | }
148 | });
149 | alert('已注册获取“分享到QZone”状态事件');
150 | };
151 |
152 |
153 | // 3 智能接口
154 | var voice = {
155 | localId: '',
156 | serverId: ''
157 | };
158 | // 3.1 识别音频并返回识别结果
159 | document.querySelector('#translateVoice').onclick = function () {
160 | if (voice.localId == '') {
161 | alert('请先使用 startRecord 接口录制一段声音');
162 | return;
163 | }
164 | wx.translateVoice({
165 | localId: voice.localId,
166 | complete: function (res) {
167 | if (res.hasOwnProperty('translateResult')) {
168 | alert('识别结果:' + res.translateResult);
169 | } else {
170 | alert('无法识别');
171 | }
172 | }
173 | });
174 | };
175 |
176 | // 4 音频接口
177 | // 4.2 开始录音
178 | document.querySelector('#startRecord').onclick = function () {
179 | wx.startRecord({
180 | cancel: function () {
181 | alert('用户拒绝授权录音');
182 | }
183 | });
184 | };
185 |
186 | // 4.3 停止录音
187 | document.querySelector('#stopRecord').onclick = function () {
188 | wx.stopRecord({
189 | success: function (res) {
190 | voice.localId = res.localId;
191 | },
192 | fail: function (res) {
193 | alert(JSON.stringify(res));
194 | }
195 | });
196 | };
197 |
198 | // 4.4 监听录音自动停止
199 | wx.onVoiceRecordEnd({
200 | complete: function (res) {
201 | voice.localId = res.localId;
202 | alert('录音时间已超过一分钟');
203 | }
204 | });
205 |
206 | // 4.5 播放音频
207 | document.querySelector('#playVoice').onclick = function () {
208 | if (voice.localId == '') {
209 | alert('请先使用 startRecord 接口录制一段声音');
210 | return;
211 | }
212 | wx.playVoice({
213 | localId: voice.localId
214 | });
215 | };
216 |
217 | // 4.6 暂停播放音频
218 | document.querySelector('#pauseVoice').onclick = function () {
219 | wx.pauseVoice({
220 | localId: voice.localId
221 | });
222 | };
223 |
224 | // 4.7 停止播放音频
225 | document.querySelector('#stopVoice').onclick = function () {
226 | wx.stopVoice({
227 | localId: voice.localId
228 | });
229 | };
230 |
231 | // 4.8 监听录音播放停止
232 | wx.onVoicePlayEnd({
233 | complete: function (res) {
234 | alert('录音(' + res.localId + ')播放结束');
235 | }
236 | });
237 |
238 | // 4.8 上传语音
239 | document.querySelector('#uploadVoice').onclick = function () {
240 | if (voice.localId == '') {
241 | alert('请先使用 startRecord 接口录制一段声音');
242 | return;
243 | }
244 | wx.uploadVoice({
245 | localId: voice.localId,
246 | success: function (res) {
247 | alert('上传语音成功,serverId 为' + res.serverId);
248 | voice.serverId = res.serverId;
249 | }
250 | });
251 | };
252 |
253 | // 4.9 下载语音
254 | document.querySelector('#downloadVoice').onclick = function () {
255 | if (voice.serverId == '') {
256 | alert('请先使用 uploadVoice 上传声音');
257 | return;
258 | }
259 | wx.downloadVoice({
260 | serverId: voice.serverId,
261 | success: function (res) {
262 | alert('下载语音成功,localId 为' + res.localId);
263 | voice.localId = res.localId;
264 | }
265 | });
266 | };
267 |
268 | // 5 图片接口
269 | // 5.1 拍照、本地选图
270 | var images = {
271 | localId: [],
272 | serverId: []
273 | };
274 | document.querySelector('#chooseImage').onclick = function () {
275 | wx.chooseImage({
276 | success: function (res) {
277 | images.localId = res.localIds;
278 | alert('已选择 ' + res.localIds.length + ' 张图片');
279 | }
280 | });
281 | };
282 |
283 | // 5.2 图片预览
284 | document.querySelector('#previewImage').onclick = function () {
285 | wx.previewImage({
286 | current: 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',
287 | urls: [
288 | 'http://img3.douban.com/view/photo/photo/public/p2152117150.jpg',
289 | 'http://img5.douban.com/view/photo/photo/public/p1353993776.jpg',
290 | 'http://img3.douban.com/view/photo/photo/public/p2152134700.jpg'
291 | ]
292 | });
293 | };
294 |
295 | // 5.3 上传图片
296 | document.querySelector('#uploadImage').onclick = function () {
297 | if (images.localId.length == 0) {
298 | alert('请先使用 chooseImage 接口选择图片');
299 | return;
300 | }
301 | var i = 0, length = images.localId.length;
302 | images.serverId = [];
303 | function upload() {
304 | wx.uploadImage({
305 | localId: images.localId[i],
306 | success: function (res) {
307 | i++;
308 | //alert('已上传:' + i + '/' + length);
309 | images.serverId.push(res.serverId);
310 | if (i < length) {
311 | upload();
312 | }
313 | },
314 | fail: function (res) {
315 | alert(JSON.stringify(res));
316 | }
317 | });
318 | }
319 | upload();
320 | };
321 |
322 | // 5.4 下载图片
323 | document.querySelector('#downloadImage').onclick = function () {
324 | if (images.serverId.length === 0) {
325 | alert('请先使用 uploadImage 上传图片');
326 | return;
327 | }
328 | var i = 0, length = images.serverId.length;
329 | images.localId = [];
330 | function download() {
331 | wx.downloadImage({
332 | serverId: images.serverId[i],
333 | success: function (res) {
334 | i++;
335 | alert('已下载:' + i + '/' + length);
336 | images.localId.push(res.localId);
337 | if (i < length) {
338 | download();
339 | }
340 | }
341 | });
342 | }
343 | download();
344 | };
345 |
346 | // 6 设备信息接口
347 | // 6.1 获取当前网络状态
348 | document.querySelector('#getNetworkType').onclick = function () {
349 | wx.getNetworkType({
350 | success: function (res) {
351 | alert(res.networkType);
352 | },
353 | fail: function (res) {
354 | alert(JSON.stringify(res));
355 | }
356 | });
357 | };
358 |
359 | // 7 地理位置接口
360 | // 7.1 查看地理位置
361 | document.querySelector('#openLocation').onclick = function () {
362 | wx.openLocation({
363 | latitude: 23.099994,
364 | longitude: 113.324520,
365 | name: 'TIT 创意园',
366 | address: '广州市海珠区新港中路 397 号',
367 | scale: 14,
368 | infoUrl: 'http://weixin.qq.com'
369 | });
370 | };
371 |
372 | // 7.2 获取当前地理位置
373 | document.querySelector('#getLocation').onclick = function () {
374 | wx.getLocation({
375 | success: function (res) {
376 | alert(JSON.stringify(res));
377 | },
378 | cancel: function (res) {
379 | alert('用户拒绝授权获取地理位置');
380 | }
381 | });
382 | };
383 |
384 | // 8 界面操作接口
385 | // 8.1 隐藏右上角菜单
386 | document.querySelector('#hideOptionMenu').onclick = function () {
387 | wx.hideOptionMenu();
388 | };
389 |
390 | // 8.2 显示右上角菜单
391 | document.querySelector('#showOptionMenu').onclick = function () {
392 | wx.showOptionMenu();
393 | };
394 |
395 | // 8.3 批量隐藏菜单项
396 | document.querySelector('#hideMenuItems').onclick = function () {
397 | wx.hideMenuItems({
398 | menuList: [
399 | 'menuItem:readMode', // 阅读模式
400 | 'menuItem:share:timeline', // 分享到朋友圈
401 | 'menuItem:copyUrl' // 复制链接
402 | ],
403 | success: function (res) {
404 | alert('已隐藏“阅读模式”,“分享到朋友圈”,“复制链接”等按钮');
405 | },
406 | fail: function (res) {
407 | alert(JSON.stringify(res));
408 | }
409 | });
410 | };
411 |
412 | // 8.4 批量显示菜单项
413 | document.querySelector('#showMenuItems').onclick = function () {
414 | wx.showMenuItems({
415 | menuList: [
416 | 'menuItem:readMode', // 阅读模式
417 | 'menuItem:share:timeline', // 分享到朋友圈
418 | 'menuItem:copyUrl' // 复制链接
419 | ],
420 | success: function (res) {
421 | alert('已显示“阅读模式”,“分享到朋友圈”,“复制链接”等按钮');
422 | },
423 | fail: function (res) {
424 | alert(JSON.stringify(res));
425 | }
426 | });
427 | };
428 |
429 | // 8.5 隐藏所有非基本菜单项
430 | document.querySelector('#hideAllNonBaseMenuItem').onclick = function () {
431 | wx.hideAllNonBaseMenuItem({
432 | success: function () {
433 | alert('已隐藏所有非基本菜单项');
434 | }
435 | });
436 | };
437 |
438 | // 8.6 显示所有被隐藏的非基本菜单项
439 | document.querySelector('#showAllNonBaseMenuItem').onclick = function () {
440 | wx.showAllNonBaseMenuItem({
441 | success: function () {
442 | alert('已显示所有非基本菜单项');
443 | }
444 | });
445 | };
446 |
447 | // 8.7 关闭当前窗口
448 | document.querySelector('#closeWindow').onclick = function () {
449 | wx.closeWindow();
450 | };
451 |
452 | // 9 微信原生接口
453 | // 9.1.1 扫描二维码并返回结果
454 | document.querySelector('#scanQRCode0').onclick = function () {
455 | wx.scanQRCode();
456 | };
457 | // 9.1.2 扫描二维码并返回结果
458 | document.querySelector('#scanQRCode1').onclick = function () {
459 | wx.scanQRCode({
460 | needResult: 1,
461 | desc: 'scanQRCode desc',
462 | success: function (res) {
463 | alert(JSON.stringify(res));
464 | }
465 | });
466 | };
467 |
468 | // 10 微信支付接口
469 | // 10.1 发起一个支付请求
470 | document.querySelector('#chooseWXPay').onclick = function () {
471 | // 注意:此 Demo 使用 2.7 版本支付接口实现,建议使用此接口时参考微信支付相关最新文档。
472 | wx.chooseWXPay({
473 | timestamp: 1414723227,
474 | nonceStr: 'noncestr',
475 | package: 'addition=action_id%3dgaby1234%26limit_pay%3d&bank_type=WX&body=innertest&fee_type=1&input_charset=GBK¬ify_url=http%3A%2F%2F120.204.206.246%2Fcgi-bin%2Fmmsupport-bin%2Fnotifypay&out_trade_no=1414723227818375338&partner=1900000109&spbill_create_ip=127.0.0.1&total_fee=1&sign=432B647FE95C7BF73BCD177CEECBEF8D',
476 | signType: 'SHA1', // 注意:新版支付接口使用 MD5 加密
477 | paySign: 'bd5b1933cda6e9548862944836a9b52e8c9a2b69'
478 | });
479 | };
480 |
481 | // 11.3 跳转微信商品页
482 | document.querySelector('#openProductSpecificView').onclick = function () {
483 | wx.openProductSpecificView({
484 | productId: 'pDF3iY_m2M7EQ5EKKKWd95kAxfNw',
485 | extInfo: '123'
486 | });
487 | };
488 |
489 | // 12 微信卡券接口
490 | // 12.1 添加卡券
491 | document.querySelector('#addCard').onclick = function () {
492 | wx.addCard({
493 | cardList: [
494 | {
495 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo',
496 | cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"ad9cf9463610bc8752c95084716581d52cd33aa0"}'
497 | },
498 | {
499 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo',
500 | cardExt: '{"code": "", "openid": "", "timestamp": "1418301401", "signature":"ad9cf9463610bc8752c95084716581d52cd33aa0"}'
501 | }
502 | ],
503 | success: function (res) {
504 | alert('已添加卡券:' + JSON.stringify(res.cardList));
505 | },
506 | cancel: function (res) {
507 | alert(JSON.stringify(res))
508 | }
509 | });
510 | };
511 |
512 | var codes = [];
513 | // 12.2 选择卡券
514 | document.querySelector('#chooseCard').onclick = function () {
515 | wx.chooseCard({
516 | cardSign: '6caa49f4a5af3d64ac247e1f563e5b5eb94619ad',
517 | timestamp: 1437997723,
518 | nonceStr: 'k0hGdSXKZEj3Min5',
519 | success: function (res) {
520 | res.cardList = JSON.parse(res.cardList);
521 | encrypt_code = res.cardList[0]['encrypt_code'];
522 | alert('已选择卡券:' + JSON.stringify(res.cardList));
523 | decryptCode(encrypt_code, function (code) {
524 | codes.push(code);
525 | });
526 | },
527 | cancel: function (res) {
528 | alert(JSON.stringify(res))
529 | }
530 | });
531 | };
532 |
533 | // 12.3 查看卡券
534 | document.querySelector('#openCard').onclick = function () {
535 | if (codes.length < 1) {
536 | alert('请先使用 chooseCard 接口选择卡券。');
537 | return false;
538 | }
539 | var cardList = [];
540 | for (var i = 0; i < codes.length; i++) {
541 | cardList.push({
542 | cardId: 'pDF3iY9tv9zCGCj4jTXFOo1DxHdo',
543 | code: codes[i]
544 | });
545 | }
546 | wx.openCard({
547 | cardList: cardList,
548 | cancel: function (res) {
549 | alert(JSON.stringify(res))
550 | }
551 | });
552 | };
553 |
554 | var shareData = {
555 | title: '微信JS-SDK Demo',
556 | desc: '微信JS-SDK,帮助第三方为用户提供更优质的移动web服务',
557 | link: 'http://demo.open.weixin.qq.com/jssdk/',
558 | imgUrl: 'http://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRt8Qia4lv7k3M9J1SKqKCImxJCt7j9rHYicKDI45jRPBxdzdyREWnk0ia0N5TMnMfth7SdxtzMvVgXg/0'
559 | };
560 | wx.onMenuShareAppMessage(shareData);
561 | wx.onMenuShareTimeline(shareData);
562 |
563 | function decryptCode(code, callback) {
564 | $.getJSON('/jssdk/decrypt_code.php?code=' + encodeURI(code), function (res) {
565 | if (res.errcode == 0) {
566 | codes.push(res.code);
567 | }
568 | });
569 | }
570 | });
571 |
572 | wx.error(function (res) {
573 | alert(res.errMsg);
574 | });
575 |
--------------------------------------------------------------------------------
/jssdk/php/sample.php:
--------------------------------------------------------------------------------
1 | GetSignPackage();
5 | ?>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
80 |
81 |
82 | 判断当前客户端是否支持指定JS接口
83 |
84 | checkJsApi
85 |
86 |
87 |
88 | 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口
89 |
92 | 获取“分享给朋友”按钮点击状态及自定义分享内容接口
93 |
96 | 获取“分享到QQ”按钮点击状态及自定义分享内容接口
97 |
100 | 获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口
101 |
104 | 获取“分享到QZone”按钮点击状态及自定义分享内容接口
105 |
108 |
109 |
110 | 拍照或从手机相册中选图接口
111 |
112 | chooseImage
113 |
114 | 预览图片接口
115 |
116 | previewImage
117 |
118 | 上传图片接口
119 |
120 | uploadImage
121 |
122 | 下载图片接口
123 |
124 | downloadImage
125 |
126 |
127 |
128 | 开始录音接口
129 |
130 | startRecord
131 |
132 | 停止录音接口
133 |
134 | stopRecord
135 |
136 | 播放语音接口
137 |
138 | playVoice
139 |
140 | 暂停播放接口
141 |
142 | pauseVoice
143 |
144 | 停止播放接口
145 |
146 | stopVoice
147 |
148 | 上传语音接口
149 |
150 | uploadVoice
151 |
152 | 下载语音接口
153 |
154 | downloadVoice
155 |
156 |
157 |
158 | 识别音频并返回识别结果接口
159 |
160 | translateVoice
161 |
162 |
163 |
164 | 获取网络状态接口
165 |
166 | getNetworkType
167 |
168 |
169 |
170 | 使用微信内置地图查看位置接口
171 |
172 | openLocation
173 |
174 | 获取地理位置接口
175 |
176 | getLocation
177 |
178 |
179 |
180 | 隐藏右上角菜单接口
181 |
184 | 显示右上角菜单接口
185 |
188 | 关闭当前网页窗口接口
189 |
190 | closeWindow
191 |
192 | 批量隐藏功能按钮接口
193 |
196 | 批量显示功能按钮接口
197 |
200 | 隐藏所有非基础按钮接口
201 |
204 | 显示所有功能按钮接口
205 |
208 |
209 |
210 | 调起微信扫一扫接口
211 |
212 | scanQRCode(微信处理结果)
213 |
214 |
215 | scanQRCode(直接返回结果)
216 |
217 |
218 |
219 | 跳转微信商品页接口
220 |
221 | openProductSpecificView
222 |
223 |
224 |
225 | 批量添加卡券接口
226 |
227 | addCard
228 |
229 | 调起适用于门店的卡券列表并获取用户选择列表
230 |
231 | chooseCard
232 |
233 | 查看微信卡包中的卡券接口
234 |
235 | openCard
236 |
237 |
238 |
239 | 发起一个微信支付请求
240 |
241 | chooseWXPay
242 |
243 |
244 |
245 |
246 |
247 |
880 |
881 |
882 |
883 |
--------------------------------------------------------------------------------
/dist/js/angular-route.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.2.5
3 | * (c) 2010-2014 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 | (function(window, angular, undefined) {'use strict';
7 |
8 | /**
9 | * @ngdoc overview
10 | * @name ngRoute
11 | * @description
12 | *
13 | * # ngRoute
14 | *
15 | * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
16 | *
17 | * ## Example
18 | * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
19 | *
20 | * {@installModule route}
21 | *
22 | *
23 | */
24 | /* global -ngRouteModule */
25 | var ngRouteModule = angular.module('ngRoute', ['ng']).
26 | provider('$route', $RouteProvider);
27 |
28 | /**
29 | * @ngdoc object
30 | * @name ngRoute.$routeProvider
31 | * @function
32 | *
33 | * @description
34 | *
35 | * Used for configuring routes.
36 | *
37 | * ## Example
38 | * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
39 | *
40 | * ## Dependencies
41 | * Requires the {@link ngRoute `ngRoute`} module to be installed.
42 | */
43 | function $RouteProvider(){
44 | function inherit(parent, extra) {
45 | return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
46 | }
47 |
48 | var routes = {};
49 |
50 | /**
51 | * @ngdoc method
52 | * @name ngRoute.$routeProvider#when
53 | * @methodOf ngRoute.$routeProvider
54 | *
55 | * @param {string} path Route path (matched against `$location.path`). If `$location.path`
56 | * contains redundant trailing slash or is missing one, the route will still match and the
57 | * `$location.path` will be updated to add or drop the trailing slash to exactly match the
58 | * route definition.
59 | *
60 | * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
61 | * to the next slash are matched and stored in `$routeParams` under the given `name`
62 | * when the route matches.
63 | * * `path` can contain named groups starting with a colon and ending with a star:
64 | * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
65 | * when the route matches.
66 | * * `path` can contain optional named groups with a question mark: e.g.`:name?`.
67 | *
68 | * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
69 | * `/color/brown/largecode/code/with/slashs/edit` and extract:
70 | *
71 | * * `color: brown`
72 | * * `largecode: code/with/slashs`.
73 | *
74 | *
75 | * @param {Object} route Mapping information to be assigned to `$route.current` on route
76 | * match.
77 | *
78 | * Object properties:
79 | *
80 | * - `controller` 鈥� `{(string|function()=}` 鈥� Controller fn that should be associated with
81 | * newly created scope or the name of a {@link angular.Module#controller registered
82 | * controller} if passed as a string.
83 | * - `controllerAs` 鈥� `{string=}` 鈥� A controller alias name. If present the controller will be
84 | * published to scope under the `controllerAs` name.
85 | * - `template` 鈥� `{string=|function()=}` 鈥� html template as a string or a function that
86 | * returns an html template as a string which should be used by {@link
87 | * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
88 | * This property takes precedence over `templateUrl`.
89 | *
90 | * If `template` is a function, it will be called with the following parameters:
91 | *
92 | * - `{Array.}` - route parameters extracted from the current
93 | * `$location.path()` by applying the current route
94 | *
95 | * - `templateUrl` 鈥� `{string=|function()=}` 鈥� path or function that returns a path to an html
96 | * template that should be used by {@link ngRoute.directive:ngView ngView}.
97 | *
98 | * If `templateUrl` is a function, it will be called with the following parameters:
99 | *
100 | * - `{Array.}` - route parameters extracted from the current
101 | * `$location.path()` by applying the current route
102 | *
103 | * - `resolve` - `{Object.=}` - An optional map of dependencies which should
104 | * be injected into the controller. If any of these dependencies are promises, the router
105 | * will wait for them all to be resolved or one to be rejected before the controller is
106 | * instantiated.
107 | * If all the promises are resolved successfully, the values of the resolved promises are
108 | * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
109 | * fired. If any of the promises are rejected the
110 | * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
111 | * is:
112 | *
113 | * - `key` 鈥� `{string}`: a name of a dependency to be injected into the controller.
114 | * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
115 | * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
116 | * and the return value is treated as the dependency. If the result is a promise, it is
117 | * resolved before its value is injected into the controller. Be aware that
118 | * `ngRoute.$routeParams` will still refer to the previous route within these resolve
119 | * functions. Use `$route.current.params` to access the new route parameters, instead.
120 | *
121 | * - `redirectTo` 鈥� {(string|function())=} 鈥� value to update
122 | * {@link ng.$location $location} path with and trigger route redirection.
123 | *
124 | * If `redirectTo` is a function, it will be called with the following parameters:
125 | *
126 | * - `{Object.}` - route parameters extracted from the current
127 | * `$location.path()` by applying the current route templateUrl.
128 | * - `{string}` - current `$location.path()`
129 | * - `{Object}` - current `$location.search()`
130 | *
131 | * The custom `redirectTo` function is expected to return a string which will be used
132 | * to update `$location.path()` and `$location.search()`.
133 | *
134 | * - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
135 | * or `$location.hash()` changes.
136 | *
137 | * If the option is set to `false` and url in the browser changes, then
138 | * `$routeUpdate` event is broadcasted on the root scope.
139 | *
140 | * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
141 | *
142 | * If the option is set to `true`, then the particular route can be matched without being
143 | * case sensitive
144 | *
145 | * @returns {Object} self
146 | *
147 | * @description
148 | * Adds a new route definition to the `$route` service.
149 | */
150 | this.when = function(path, route) {
151 | routes[path] = angular.extend(
152 | {reloadOnSearch: true},
153 | route,
154 | path && pathRegExp(path, route)
155 | );
156 |
157 | // create redirection for trailing slashes
158 | if (path) {
159 | var redirectPath = (path[path.length-1] == '/')
160 | ? path.substr(0, path.length-1)
161 | : path +'/';
162 |
163 | routes[redirectPath] = angular.extend(
164 | {redirectTo: path},
165 | pathRegExp(redirectPath, route)
166 | );
167 | }
168 |
169 | return this;
170 | };
171 |
172 | /**
173 | * @param path {string} path
174 | * @param opts {Object} options
175 | * @return {?Object}
176 | *
177 | * @description
178 | * Normalizes the given path, returning a regular expression
179 | * and the original path.
180 | *
181 | * Inspired by pathRexp in visionmedia/express/lib/utils.js.
182 | */
183 | function pathRegExp(path, opts) {
184 | var insensitive = opts.caseInsensitiveMatch,
185 | ret = {
186 | originalPath: path,
187 | regexp: path
188 | },
189 | keys = ret.keys = [];
190 |
191 | path = path
192 | .replace(/([().])/g, '\\$1')
193 | .replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
194 | var optional = option === '?' ? option : null;
195 | var star = option === '*' ? option : null;
196 | keys.push({ name: key, optional: !!optional });
197 | slash = slash || '';
198 | return ''
199 | + (optional ? '' : slash)
200 | + '(?:'
201 | + (optional ? slash : '')
202 | + (star && '(.+?)' || '([^/]+)')
203 | + (optional || '')
204 | + ')'
205 | + (optional || '');
206 | })
207 | .replace(/([\/$\*])/g, '\\$1');
208 |
209 | ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
210 | return ret;
211 | }
212 |
213 | /**
214 | * @ngdoc method
215 | * @name ngRoute.$routeProvider#otherwise
216 | * @methodOf ngRoute.$routeProvider
217 | *
218 | * @description
219 | * Sets route definition that will be used on route change when no other route definition
220 | * is matched.
221 | *
222 | * @param {Object} params Mapping information to be assigned to `$route.current`.
223 | * @returns {Object} self
224 | */
225 | this.otherwise = function(params) {
226 | this.when(null, params);
227 | return this;
228 | };
229 |
230 |
231 | this.$get = ['$rootScope',
232 | '$location',
233 | '$routeParams',
234 | '$q',
235 | '$injector',
236 | '$http',
237 | '$templateCache',
238 | '$sce',
239 | function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
240 |
241 | /**
242 | * @ngdoc object
243 | * @name ngRoute.$route
244 | * @requires $location
245 | * @requires $routeParams
246 | *
247 | * @property {Object} current Reference to the current route definition.
248 | * The route definition contains:
249 | *
250 | * - `controller`: The controller constructor as define in route definition.
251 | * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
252 | * controller instantiation. The `locals` contain
253 | * the resolved values of the `resolve` map. Additionally the `locals` also contain:
254 | *
255 | * - `$scope` - The current route scope.
256 | * - `$template` - The current route template HTML.
257 | *
258 | * @property {Array.} routes Array of all configured routes.
259 | *
260 | * @description
261 | * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
262 | * It watches `$location.url()` and tries to map the path to an existing route definition.
263 | *
264 | * Requires the {@link ngRoute `ngRoute`} module to be installed.
265 | *
266 | * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
267 | *
268 | * The `$route` service is typically used in conjunction with the
269 | * {@link ngRoute.directive:ngView `ngView`} directive and the
270 | * {@link ngRoute.$routeParams `$routeParams`} service.
271 | *
272 | * @example
273 | This example shows how changing the URL hash causes the `$route` to match a route against the
274 | URL, and the `ngView` pulls in the partial.
275 |
276 | Note that this example is using {@link ng.directive:script inlined templates}
277 | to get it working on jsfiddle as well.
278 |
279 |
280 |
281 |
282 | Choose:
283 |
Moby |
284 |
Moby: Ch1 |
285 |
Gatsby |
286 |
Gatsby: Ch4 |
287 |
Scarlet Letter
288 |
289 |
290 |
291 |
292 |
$location.path() = {{$location.path()}}
293 |
$route.current.templateUrl = {{$route.current.templateUrl}}
294 |
$route.current.params = {{$route.current.params}}
295 |
$route.current.scope.name = {{$route.current.scope.name}}
296 |
$routeParams = {{$routeParams}}
297 |
298 |
299 |
300 |
301 | controller: {{name}}
302 | Book Id: {{params.bookId}}
303 |
304 |
305 |
306 | controller: {{name}}
307 | Book Id: {{params.bookId}}
308 | Chapter Id: {{params.chapterId}}
309 |
310 |
311 |
312 | angular.module('ngViewExample', ['ngRoute'])
313 |
314 | .config(function($routeProvider, $locationProvider) {
315 | $routeProvider.when('/Book/:bookId', {
316 | templateUrl: 'book.html',
317 | controller: BookCntl,
318 | resolve: {
319 | // I will cause a 1 second delay
320 | delay: function($q, $timeout) {
321 | var delay = $q.defer();
322 | $timeout(delay.resolve, 1000);
323 | return delay.promise;
324 | }
325 | }
326 | });
327 | $routeProvider.when('/Book/:bookId/ch/:chapterId', {
328 | templateUrl: 'chapter.html',
329 | controller: ChapterCntl
330 | });
331 |
332 | // configure html5 to get links working on jsfiddle
333 | $locationProvider.html5Mode(true);
334 | });
335 |
336 | function MainCntl($scope, $route, $routeParams, $location) {
337 | $scope.$route = $route;
338 | $scope.$location = $location;
339 | $scope.$routeParams = $routeParams;
340 | }
341 |
342 | function BookCntl($scope, $routeParams) {
343 | $scope.name = "BookCntl";
344 | $scope.params = $routeParams;
345 | }
346 |
347 | function ChapterCntl($scope, $routeParams) {
348 | $scope.name = "ChapterCntl";
349 | $scope.params = $routeParams;
350 | }
351 |
352 |
353 |
354 | it('should load and compile correct template', function() {
355 | element('a:contains("Moby: Ch1")').click();
356 | var content = element('.doc-example-live [ng-view]').text();
357 | expect(content).toMatch(/controller\: ChapterCntl/);
358 | expect(content).toMatch(/Book Id\: Moby/);
359 | expect(content).toMatch(/Chapter Id\: 1/);
360 |
361 | element('a:contains("Scarlet")').click();
362 | sleep(2); // promises are not part of scenario waiting
363 | content = element('.doc-example-live [ng-view]').text();
364 | expect(content).toMatch(/controller\: BookCntl/);
365 | expect(content).toMatch(/Book Id\: Scarlet/);
366 | });
367 |
368 |
369 | */
370 |
371 | /**
372 | * @ngdoc event
373 | * @name ngRoute.$route#$routeChangeStart
374 | * @eventOf ngRoute.$route
375 | * @eventType broadcast on root scope
376 | * @description
377 | * Broadcasted before a route change. At this point the route services starts
378 | * resolving all of the dependencies needed for the route change to occurs.
379 | * Typically this involves fetching the view template as well as any dependencies
380 | * defined in `resolve` route property. Once all of the dependencies are resolved
381 | * `$routeChangeSuccess` is fired.
382 | *
383 | * @param {Object} angularEvent Synthetic event object.
384 | * @param {Route} next Future route information.
385 | * @param {Route} current Current route information.
386 | */
387 |
388 | /**
389 | * @ngdoc event
390 | * @name ngRoute.$route#$routeChangeSuccess
391 | * @eventOf ngRoute.$route
392 | * @eventType broadcast on root scope
393 | * @description
394 | * Broadcasted after a route dependencies are resolved.
395 | * {@link ngRoute.directive:ngView ngView} listens for the directive
396 | * to instantiate the controller and render the view.
397 | *
398 | * @param {Object} angularEvent Synthetic event object.
399 | * @param {Route} current Current route information.
400 | * @param {Route|Undefined} previous Previous route information, or undefined if current is
401 | * first route entered.
402 | */
403 |
404 | /**
405 | * @ngdoc event
406 | * @name ngRoute.$route#$routeChangeError
407 | * @eventOf ngRoute.$route
408 | * @eventType broadcast on root scope
409 | * @description
410 | * Broadcasted if any of the resolve promises are rejected.
411 | *
412 | * @param {Object} angularEvent Synthetic event object
413 | * @param {Route} current Current route information.
414 | * @param {Route} previous Previous route information.
415 | * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
416 | */
417 |
418 | /**
419 | * @ngdoc event
420 | * @name ngRoute.$route#$routeUpdate
421 | * @eventOf ngRoute.$route
422 | * @eventType broadcast on root scope
423 | * @description
424 | *
425 | * The `reloadOnSearch` property has been set to false, and we are reusing the same
426 | * instance of the Controller.
427 | */
428 |
429 | var forceReload = false,
430 | $route = {
431 | routes: routes,
432 |
433 | /**
434 | * @ngdoc method
435 | * @name ngRoute.$route#reload
436 | * @methodOf ngRoute.$route
437 | *
438 | * @description
439 | * Causes `$route` service to reload the current route even if
440 | * {@link ng.$location $location} hasn't changed.
441 | *
442 | * As a result of that, {@link ngRoute.directive:ngView ngView}
443 | * creates new scope, reinstantiates the controller.
444 | */
445 | reload: function() {
446 | forceReload = true;
447 | $rootScope.$evalAsync(updateRoute);
448 | }
449 | };
450 |
451 | $rootScope.$on('$locationChangeSuccess', updateRoute);
452 |
453 | return $route;
454 |
455 | /////////////////////////////////////////////////////
456 |
457 | /**
458 | * @param on {string} current url
459 | * @param route {Object} route regexp to match the url against
460 | * @return {?Object}
461 | *
462 | * @description
463 | * Check if the route matches the current url.
464 | *
465 | * Inspired by match in
466 | * visionmedia/express/lib/router/router.js.
467 | */
468 | function switchRouteMatcher(on, route) {
469 | var keys = route.keys,
470 | params = {};
471 |
472 | if (!route.regexp) return null;
473 |
474 | var m = route.regexp.exec(on);
475 | if (!m) return null;
476 |
477 | for (var i = 1, len = m.length; i < len; ++i) {
478 | var key = keys[i - 1];
479 |
480 | var val = 'string' == typeof m[i]
481 | ? decodeURIComponent(m[i])
482 | : m[i];
483 |
484 | if (key && val) {
485 | params[key.name] = val;
486 | }
487 | }
488 | return params;
489 | }
490 |
491 | function updateRoute() {
492 | var next = parseRoute(),
493 | last = $route.current;
494 |
495 | if (next && last && next.$$route === last.$$route
496 | && angular.equals(next.pathParams, last.pathParams)
497 | && !next.reloadOnSearch && !forceReload) {
498 | last.params = next.params;
499 | angular.copy(last.params, $routeParams);
500 | $rootScope.$broadcast('$routeUpdate', last);
501 | } else if (next || last) {
502 | forceReload = false;
503 | $rootScope.$broadcast('$routeChangeStart', next, last);
504 | $route.current = next;
505 | if (next) {
506 | if (next.redirectTo) {
507 | if (angular.isString(next.redirectTo)) {
508 | $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
509 | .replace();
510 | } else {
511 | $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
512 | .replace();
513 | }
514 | }
515 | }
516 |
517 | $q.when(next).
518 | then(function() {
519 | if (next) {
520 | var locals = angular.extend({}, next.resolve),
521 | template, templateUrl;
522 |
523 | angular.forEach(locals, function(value, key) {
524 | locals[key] = angular.isString(value) ?
525 | $injector.get(value) : $injector.invoke(value);
526 | });
527 |
528 | if (angular.isDefined(template = next.template)) {
529 | if (angular.isFunction(template)) {
530 | template = template(next.params);
531 | }
532 | } else if (angular.isDefined(templateUrl = next.templateUrl)) {
533 | if (angular.isFunction(templateUrl)) {
534 | templateUrl = templateUrl(next.params);
535 | }
536 | templateUrl = $sce.getTrustedResourceUrl(templateUrl);
537 | if (angular.isDefined(templateUrl)) {
538 | next.loadedTemplateUrl = templateUrl;
539 | template = $http.get(templateUrl, {cache: $templateCache}).
540 | then(function(response) { return response.data; });
541 | }
542 | }
543 | if (angular.isDefined(template)) {
544 | locals['$template'] = template;
545 | }
546 | return $q.all(locals);
547 | }
548 | }).
549 | // after route change
550 | then(function(locals) {
551 | if (next == $route.current) {
552 | if (next) {
553 | next.locals = locals;
554 | angular.copy(next.params, $routeParams);
555 | }
556 | $rootScope.$broadcast('$routeChangeSuccess', next, last);
557 | }
558 | }, function(error) {
559 | if (next == $route.current) {
560 | $rootScope.$broadcast('$routeChangeError', next, last, error);
561 | }
562 | });
563 | }
564 | }
565 |
566 |
567 | /**
568 | * @returns the current active route, by matching it against the URL
569 | */
570 | function parseRoute() {
571 | // Match a route
572 | var params, match;
573 | angular.forEach(routes, function(route, path) {
574 | if (!match && (params = switchRouteMatcher($location.path(), route))) {
575 | match = inherit(route, {
576 | params: angular.extend({}, $location.search(), params),
577 | pathParams: params});
578 | match.$$route = route;
579 | }
580 | });
581 | // No route matched; fallback to "otherwise" route
582 | return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
583 | }
584 |
585 | /**
586 | * @returns interpolation of the redirect path with the parameters
587 | */
588 | function interpolate(string, params) {
589 | var result = [];
590 | angular.forEach((string||'').split(':'), function(segment, i) {
591 | if (i === 0) {
592 | result.push(segment);
593 | } else {
594 | var segmentMatch = segment.match(/(\w+)(.*)/);
595 | var key = segmentMatch[1];
596 | result.push(params[key]);
597 | result.push(segmentMatch[2] || '');
598 | delete params[key];
599 | }
600 | });
601 | return result.join('');
602 | }
603 | }];
604 | }
605 |
606 | ngRouteModule.provider('$routeParams', $RouteParamsProvider);
607 |
608 |
609 | /**
610 | * @ngdoc object
611 | * @name ngRoute.$routeParams
612 | * @requires $route
613 | *
614 | * @description
615 | * The `$routeParams` service allows you to retrieve the current set of route parameters.
616 | *
617 | * Requires the {@link ngRoute `ngRoute`} module to be installed.
618 | *
619 | * The route parameters are a combination of {@link ng.$location `$location`}'s
620 | * {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
621 | * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
622 | *
623 | * In case of parameter name collision, `path` params take precedence over `search` params.
624 | *
625 | * The service guarantees that the identity of the `$routeParams` object will remain unchanged
626 | * (but its properties will likely change) even when a route change occurs.
627 | *
628 | * Note that the `$routeParams` are only updated *after* a route change completes successfully.
629 | * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
630 | * Instead you can use `$route.current.params` to access the new route's parameters.
631 | *
632 | * @example
633 | *
634 | * // Given:
635 | * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
636 | * // Route: /Chapter/:chapterId/Section/:sectionId
637 | * //
638 | * // Then
639 | * $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
640 | *
641 | */
642 | function $RouteParamsProvider() {
643 | this.$get = function() { return {}; };
644 | }
645 |
646 | ngRouteModule.directive('ngView', ngViewFactory);
647 | ngRouteModule.directive('ngView', ngViewFillContentFactory);
648 |
649 |
650 | /**
651 | * @ngdoc directive
652 | * @name ngRoute.directive:ngView
653 | * @restrict ECA
654 | *
655 | * @description
656 | * # Overview
657 | * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
658 | * including the rendered template of the current route into the main layout (`index.html`) file.
659 | * Every time the current route changes, the included view changes with it according to the
660 | * configuration of the `$route` service.
661 | *
662 | * Requires the {@link ngRoute `ngRoute`} module to be installed.
663 | *
664 | * @animations
665 | * enter - animation is used to bring new content into the browser.
666 | * leave - animation is used to animate existing content away.
667 | *
668 | * The enter and leave animation occur concurrently.
669 | *
670 | * @scope
671 | * @priority 400
672 | * @example
673 |
674 |
675 |
676 | Choose:
677 |
Moby |
678 |
Moby: Ch1 |
679 |
Gatsby |
680 |
Gatsby: Ch4 |
681 |
Scarlet Letter
682 |
683 |
686 |
687 |
688 |
$location.path() = {{main.$location.path()}}
689 |
$route.current.templateUrl = {{main.$route.current.templateUrl}}
690 |
$route.current.params = {{main.$route.current.params}}
691 |
$route.current.scope.name = {{main.$route.current.scope.name}}
692 |
$routeParams = {{main.$routeParams}}
693 |
694 |
695 |
696 |
697 |
698 | controller: {{book.name}}
699 | Book Id: {{book.params.bookId}}
700 |
701 |
702 |
703 |
704 |
705 | controller: {{chapter.name}}
706 | Book Id: {{chapter.params.bookId}}
707 | Chapter Id: {{chapter.params.chapterId}}
708 |
709 |
710 |
711 |
712 | .view-animate-container {
713 | position:relative;
714 | height:100px!important;
715 | position:relative;
716 | background:white;
717 | border:1px solid black;
718 | height:40px;
719 | overflow:hidden;
720 | }
721 |
722 | .view-animate {
723 | padding:10px;
724 | }
725 |
726 | .view-animate.ng-enter, .view-animate.ng-leave {
727 | -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
728 | transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
729 |
730 | display:block;
731 | width:100%;
732 | border-left:1px solid black;
733 |
734 | position:absolute;
735 | top:0;
736 | left:0;
737 | right:0;
738 | bottom:0;
739 | padding:10px;
740 | }
741 |
742 | .view-animate.ng-enter {
743 | left:100%;
744 | }
745 | .view-animate.ng-enter.ng-enter-active {
746 | left:0;
747 | }
748 | .view-animate.ng-leave.ng-leave-active {
749 | left:-100%;
750 | }
751 |
752 |
753 |
754 | angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
755 | function($routeProvider, $locationProvider) {
756 | $routeProvider.when('/Book/:bookId', {
757 | templateUrl: 'book.html',
758 | controller: BookCntl,
759 | controllerAs: 'book'
760 | });
761 | $routeProvider.when('/Book/:bookId/ch/:chapterId', {
762 | templateUrl: 'chapter.html',
763 | controller: ChapterCntl,
764 | controllerAs: 'chapter'
765 | });
766 |
767 | // configure html5 to get links working on jsfiddle
768 | $locationProvider.html5Mode(true);
769 | });
770 |
771 | function MainCntl($route, $routeParams, $location) {
772 | this.$route = $route;
773 | this.$location = $location;
774 | this.$routeParams = $routeParams;
775 | }
776 |
777 | function BookCntl($routeParams) {
778 | this.name = "BookCntl";
779 | this.params = $routeParams;
780 | }
781 |
782 | function ChapterCntl($routeParams) {
783 | this.name = "ChapterCntl";
784 | this.params = $routeParams;
785 | }
786 |
787 |
788 |
789 | it('should load and compile correct template', function() {
790 | element('a:contains("Moby: Ch1")').click();
791 | var content = element('.doc-example-live [ng-view]').text();
792 | expect(content).toMatch(/controller\: ChapterCntl/);
793 | expect(content).toMatch(/Book Id\: Moby/);
794 | expect(content).toMatch(/Chapter Id\: 1/);
795 |
796 | element('a:contains("Scarlet")').click();
797 | content = element('.doc-example-live [ng-view]').text();
798 | expect(content).toMatch(/controller\: BookCntl/);
799 | expect(content).toMatch(/Book Id\: Scarlet/);
800 | });
801 |
802 |
803 | */
804 |
805 |
806 | /**
807 | * @ngdoc event
808 | * @name ngRoute.directive:ngView#$viewContentLoaded
809 | * @eventOf ngRoute.directive:ngView
810 | * @eventType emit on the current ngView scope
811 | * @description
812 | * Emitted every time the ngView content is reloaded.
813 | */
814 | ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
815 | function ngViewFactory( $route, $anchorScroll, $animate) {
816 | return {
817 | restrict: 'ECA',
818 | terminal: true,
819 | priority: 400,
820 | transclude: 'element',
821 | link: function(scope, $element, attr, ctrl, $transclude) {
822 | var currentScope,
823 | currentElement,
824 | autoScrollExp = attr.autoscroll,
825 | onloadExp = attr.onload || '';
826 |
827 | scope.$on('$routeChangeSuccess', update);
828 | update();
829 |
830 | function cleanupLastView() {
831 | if (currentScope) {
832 | currentScope.$destroy();
833 | currentScope = null;
834 | }
835 | if(currentElement) {
836 | $animate.leave(currentElement);
837 | currentElement = null;
838 | }
839 | }
840 |
841 | function update() {
842 | var locals = $route.current && $route.current.locals,
843 | template = locals && locals.$template;
844 |
845 | if (template) {
846 | var newScope = scope.$new();
847 | var current = $route.current;
848 |
849 | // Note: This will also link all children of ng-view that were contained in the original
850 | // html. If that content contains controllers, ... they could pollute/change the scope.
851 | // However, using ng-view on an element with additional content does not make sense...
852 | // Note: We can't remove them in the cloneAttchFn of $transclude as that
853 | // function is called before linking the content, which would apply child
854 | // directives to non existing elements.
855 | var clone = $transclude(newScope, function(clone) {
856 | $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
857 | if (angular.isDefined(autoScrollExp)
858 | && (!autoScrollExp || scope.$eval(autoScrollExp))) {
859 | $anchorScroll();
860 | }
861 | });
862 | cleanupLastView();
863 | });
864 |
865 | currentElement = clone;
866 | currentScope = current.scope = newScope;
867 | currentScope.$emit('$viewContentLoaded');
868 | currentScope.$eval(onloadExp);
869 | } else {
870 | cleanupLastView();
871 | }
872 | }
873 | }
874 | };
875 | }
876 |
877 | // This directive is called during the $transclude call of the first `ngView` directive.
878 | // It will replace and compile the content of the element with the loaded template.
879 | // We need this directive so that the element content is already filled when
880 | // the link function of another directive on the same element as ngView
881 | // is called.
882 | ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
883 | function ngViewFillContentFactory($compile, $controller, $route) {
884 | return {
885 | restrict: 'ECA',
886 | priority: -400,
887 | link: function(scope, $element) {
888 | var current = $route.current,
889 | locals = current.locals;
890 |
891 | $element.html(locals.$template);
892 |
893 | var link = $compile($element.contents());
894 |
895 | if (current.controller) {
896 | locals.$scope = scope;
897 | var controller = $controller(current.controller, locals);
898 | if (current.controllerAs) {
899 | scope[current.controllerAs] = controller;
900 | }
901 | $element.data('$ngControllerController', controller);
902 | $element.children().data('$ngControllerController', controller);
903 | }
904 |
905 | link(scope);
906 | }
907 | };
908 | }
909 |
910 |
911 | })(window, window.angular);
--------------------------------------------------------------------------------