├── 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 | ![这里写图片描述](http://img.blog.csdn.net/20160721173253764) 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 |
2 |
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 |
3 |
我的信息
4 |
我的登記
5 |
6 |
123
7 |
456
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 |

{{new.source}} {{new.pubDate}} 查看原文

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 |
14 |
我的信息
15 |
我的登記
16 |
17 |
wsscat
18 |
456
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 |
3 | 4 | 5 |

{{new.title}}

6 | 7 | 8 | 9 |

10 | {{new.desc}} 11 |

12 |
13 |
14 | 15 | 18 |
19 | 20 |
21 | -------------------------------------------------------------------------------- /view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |

{{new.title}}

8 | 9 |

10 | {{new.description}} 11 |

12 |
13 |
14 |
15 |

查看更多

16 |
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 |
25 | 日日头条 26 |
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 |
110 | 日日头条 111 |
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 |
17 | 79 |
80 |
81 | 82 | 判断当前客户端是否支持指定JS接口 83 | 86 | 87 | 88 | 获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 89 | 92 | 获取“分享给朋友”按钮点击状态及自定义分享内容接口 93 | 96 | 获取“分享到QQ”按钮点击状态及自定义分享内容接口 97 | 100 | 获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 101 | 104 | 获取“分享到QZone”按钮点击状态及自定义分享内容接口 105 | 108 | 109 | 110 | 拍照或从手机相册中选图接口 111 | 114 | 预览图片接口 115 | 118 | 上传图片接口 119 | 122 | 下载图片接口 123 | 126 | 127 | 128 | 开始录音接口 129 | 132 | 停止录音接口 133 | 136 | 播放语音接口 137 | 140 | 暂停播放接口 141 | 144 | 停止播放接口 145 | 148 | 上传语音接口 149 | 152 | 下载语音接口 153 | 156 | 157 | 158 | 识别音频并返回识别结果接口 159 | 162 | 163 | 164 | 获取网络状态接口 165 | 168 | 169 | 170 | 使用微信内置地图查看位置接口 171 | 174 | 获取地理位置接口 175 | 178 | 179 | 180 | 隐藏右上角菜单接口 181 | 184 | 显示右上角菜单接口 185 | 188 | 关闭当前网页窗口接口 189 | 192 | 批量隐藏功能按钮接口 193 | 196 | 批量显示功能按钮接口 197 | 200 | 隐藏所有非基础按钮接口 201 | 204 | 显示所有功能按钮接口 205 | 208 | 209 | 210 | 调起微信扫一扫接口 211 | 214 | 217 | 218 | 219 | 跳转微信商品页接口 220 | 223 | 224 | 225 | 批量添加卡券接口 226 | 229 | 调起适用于门店的卡券列表并获取用户选择列表 230 | 233 | 查看微信卡包中的卡券接口 234 | 237 | 238 | 239 | 发起一个微信支付请求 240 | 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 |
684 |
685 |
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); --------------------------------------------------------------------------------