diff --git a/test/browser/demo/sdk.html b/test/browser/demo/sdk.html
new file mode 100644
index 0000000..86d8648
--- /dev/null
+++ b/test/browser/demo/sdk.html
@@ -0,0 +1,52 @@
+
+
+
+ 百度开放云
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/browser/demo/src/sdk.js b/test/browser/demo/src/sdk.js
new file mode 100644
index 0000000..f424aa5
--- /dev/null
+++ b/test/browser/demo/src/sdk.js
@@ -0,0 +1,117 @@
+/**
+ * @file sdk.js
+ * @author leeight
+ */
+
+define(function (require) {
+ var sdk = require('baidubce-sdk');
+ var u = require('underscore');
+
+ var helper = require('./helper');
+
+ var kQueryString = getQueryString();
+
+ var exports = {};
+
+ var events = {
+ READY: 'ready',
+ FILE_CHANGE: 'fileChange',
+ UPLOAD_SUCCESS: 'uploadSuccess',
+ UPLOAD_FAILURE: 'uploadFailure',
+
+ UPLOAD: 'upload'
+ };
+
+ function fire(type, data) {
+ if (window === top) {
+ return;
+ }
+
+ parent.postMessage({
+ type: type,
+ data: data
+ }, '*');
+ }
+
+
+ /**
+ * 处理父页面发送过来的信息.
+ */
+ function handlePostMessage (evt) {
+ var originalEvent = evt.originalEvent;
+ var payload = originalEvent.data;
+
+ var type = payload.type;
+ var data = payload.data;
+
+ if (type === events.UPLOAD) {
+ var client = getClient();
+ var blob = $('#file').get(0).files[0];
+ $('#file').attr('disabled', true);
+ helper.upload(data.bucketName, data.objectName, blob, {}, client)
+ .then(function (response) {
+ fire(events.UPLOAD_SUCCESS, response);
+ })
+ .catch(function (error) {
+ fire(events.UPLOAD_FAILURE, error);
+ })
+ .fin(function () {
+ $('#file').attr('disabled', false);
+ });
+ }
+ }
+
+ function getClient() {
+ var client = new sdk.BosClient(getConfig());
+
+ // 用户显示的设置了 ak 和 sk,不需要服务器计算
+ if (kQueryString['ed']) {
+ // 如果 url 参数里面存在 ?ed=1 那么说明是本地开发模式,需要在计算
+ // 签名的时候使用真正的Host,而不是当前页面的域名
+ client.createSignature = function (credentials, httpMethod, path, params, headers) {
+ // 修复 Host 的内容
+ headers.Host = kQueryString['ed'];
+
+ var auth = new sdk.Auth(credentials.ak, credentials.sk);
+ return auth.generateAuthorization(httpMethod, path, params, headers);
+ };
+ }
+
+ return client;
+ }
+
+ function getConfig() {
+ return JSON.parse(decodeURIComponent(kQueryString['config']));
+ }
+
+ function getQueryString() {
+ return u.object(location.search.substr(1).split('&').map(function (item) {
+ var chunks = item.split('=', 2);
+ return [chunks[0], chunks[1]];
+ }));
+ }
+
+ function handleFileChange (evt) {
+ fire(events.FILE_CHANGE, evt.target.files);
+ }
+
+ exports.start = function () {
+ $(window).on('message', handlePostMessage);
+ $('#file').on('change', handleFileChange);
+
+ fire(events.READY);
+ };
+
+ return exports;
+});
+
+
+
+
+
+
+
+
+
+
+/* vim: set ts=4 sw=4 sts=4 tw=120: */
diff --git a/test/browser/demo/src/web.js b/test/browser/demo/src/web.js
new file mode 100644
index 0000000..888faea
--- /dev/null
+++ b/test/browser/demo/src/web.js
@@ -0,0 +1,140 @@
+/**
+ * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+/**
+ * 页面中需要有 jQuery,自行引用.
+ * @constructor
+ */
+function BosWebClient(options) {
+ /**
+ * 配置信息
+ * @type {Object}
+ */
+ this.options = options;
+
+ /**
+ * Bucket的名字
+ * @type {string}
+ */
+ this.bucketName = null;
+
+ /**
+ * 上传到 Bucket 的文件名,必须以 '/' 开头.
+ * @type {string}
+ */
+ this.objectName = null;
+
+ /**
+ * SDK所在页面的引用
+ * @type {Window}
+ */
+ this._sdkWinRef = null;
+
+ /**
+ * @type {Object}
+ */
+ this._eventHandlers = {};
+}
+
+BosWebClient.prototype.on = function (type, handler, opt_context) {
+ var list = this._eventHandlers[type];
+ if (!list) {
+ list = (this._eventHandlers[type] = []);
+ }
+
+ list.push({handler: handler, context: opt_context});
+};
+
+BosWebClient.prototype.dispatchEvent = function (type, data) {
+ var list = this._eventHandlers[type];
+ if (!list) {
+ return;
+ }
+
+ for (var i = 0; i < list.length; i ++) {
+ var item = list[i];
+ try {
+ item.handler.call(item.context, data);
+ }
+ catch (ex) {
+ // IGNORE
+ }
+ }
+};
+
+
+BosWebClient.prototype.fire = function (type, data) {
+ this._sdkWinRef.postMessage({
+ type: type,
+ data: data
+ }, '*');
+};
+
+/**
+ * 初始化Web Uploader
+ * @param {jQuery} container 容器的Id.
+ */
+BosWebClient.prototype.init = function (container) {
+ var config = JSON.stringify(this.options);
+ var mark = this.options.relayUrl.indexOf('?') === -1 ? '?' : '&';
+ var iframeUrl = this.options.relayUrl + mark + 'config=' + encodeURIComponent(config);
+ // var iframeUrl = config.endpoint + '/explorer/sdk.html?config=' + encodeURIComponent(config);
+
+ container.html('