八、离线Web应用实战。
通过一个简单的记事本程序——PermaNote,来解释如何使用。程序将用户的文本保存到localStorage中,并且在网络连接可用的时候,
将其上传到服务器,PermaNote只允许用户编辑单个笔记。
PermaNote应用包含3个文件,一个应用清单文件、一个html页面文件,一个实现逻辑的js文件。
Demo: http://xuanfengge.com/demo/201506/appcache/permanote.html
①.premanote.appcache部分:
CACHE MANIFEST
# PermaNote v8
permanote.html
permanote.js
NETWORK:
note
②.permanote.html部分:
<!DOCTYPEHTML>
<html manifest= permanote.appcache”>
<head>
<title>PermaNote Editor</title>
<script src=” permanote.js”></script>
<style type=”text/css”>
#editor {width:100%;height:250px}
#statusline{width:100%}
</style>
</head>
<body>
<div id=”toobar”>
<button id=”savebutton”onclick = “save()”>save</button>
<button onclick = “sync()”>SyncNote</button>
<button onclick = “applicationCache.update()”>UpdateApplication</button>
<textarea id=”editor”></textarea>
<div id=”statusline”></div>
</div>
</body>
</html>
③.permanote.js部分
status()函数用于显示状态栏消息,save()函数将笔记本保存到服务器,sync()用于确保本地与服务器文本的同步。
应用程序的时间处理程序解释:
(1).onload
尝试和服务器同步,一旦有新版本的笔记并且完成同步后,就启用编辑器窗口。
save()和sync()函数发出HTTP请求,并在XMLHttpRequest对象上注册一个onload时间处理程序来获取上传或者
下载完成的提醒。
(2).onbeforeunload
在未上传前,把当前版本的笔记数据保存到服务器上。
(3).oninput
每当textarea输入框内容发生变化时,都将其内容保存到localStorage中,并启动一个计时器。当用户停止编辑超过5秒
,将自动把数据保存到服务器。
(4).onoffline
当浏览器进入离线状态时,在状态栏显示离线消息。
(5).ononline
当浏览器回到在线状态时,同步服务器,检查是否有新版本的数据,并且保存当前版本的数据。
(6).onupdateready
如果新版本的应用已缓存,则在状态栏展示消息告知用户。
(7).onnoupdate
// 定义全局变量
var editor, statusline, savebutton, idletimer;
// 首次载入应用
window.onload = function() {
// 第一次载入时,初始化本地保存
if (localStorage.note == null) localStorage.note = "";
if (localStorage.lastModified == null) localStorage.lastModified = 0;
if (localStorage.lastSaved == null) localStorage.lastSaved = 0;
// 查找编辑器UI元素,并初始化全局变量
editor = document.getElementById("editor");
statusline = document.getElementById("statusline");
savebutton = document.getElementById("savebutton");
editor.value = localStorage.note; // 初始化编辑器,将保存的笔记数据填充到内容
editor.disabled = true; // 同步前禁止编辑
// 当输入框内容发生变化
editor.addEventListener("input",
function (e) {
// 将新的内容保存至localStorage
localStorage.note = editor.value;
localStorage.lastModified = Date.now();
// 重置闲置计时器
if (idletimer) clearTimeout(idletimer);
idletimer = setTimeout(save, 5000);
// 启用保存按钮
savebutton.disabled = false;
},
false);
// 每次载入应用程序时,尝试同步服务器
sync();
};
// 离开页面钱保存数据到服务器
window.onbeforeunload = function() {
if (localStorage.lastModified > localStorage.lastSaved)
save();
};
// 离线时,告知用户
window.onoffline = function() { status("Offline"); }
// 再次返回在线状态时,进行同步
window.ononline = function() { sync(); };
// 当有新版本应用的时候,提醒用户
// 也可使用location.reload()放大来强制刷新应用
window.applicationCache.onupdateready = function() {
status("A new version of this application is available. Reload to run it");
};
// 当没有新版本的时候也通知用户
window.applicationCache.onnoupdate = function() {
status("You are running the latest version of the application.");
};
// 状态栏显示相关信息提示
function status(msg) { statusline.innerHTML = msg; }
// 每当笔记本内容更新后,如果用户停止编辑超过5分钟
// 就会自动将笔记文本上传至服务器(在线状态下)
function save() {
if (idletimer) clearTimeout(idletimer);
idletimer = null;
if (navigator.onLine) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", "/note");
xhr.send(editor.value);
xhr.onload = function() {
localStorage.lastSaved = Date.now();
savebutton.disabled = true;
};
}
}
// 检查服务端是否有新版本的笔记,若无,则将当前版本保存到服务器端
function sync() {
if (navigator.onLine) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/note");
xhr.send();
xhr.onload = function() {
var remoteModTime = 0;
if (xhr.status == 200) {
var remoteModTime = xhr.getResponseHeader("Last-Modified");
remoteModTime = new Date(remoteModTime).getTime();
}
if (remoteModTime > localStorage.lastModified) {
status("Newer note found on server.");
var useit =
confirm("There is a newer version of the note\n" +
"on the server. Click Ok to use that version\n"+
"or click Cancel to continue editing this\n"+
"version and overwrite the server");
var now = Date.now();
if (useit) {
editor.value = localStorage.note = xhr.responseText;
localStorage.lastSaved = now;
status("Newest version downloaded.");
}
else
status("Ignoring newer version of the note.");
localStorage.lastModified = now;
}
else
status("You are editing the current version of the note.");
if (localStorage.lastModified > localStorage.lastSaved) {
save();
}
editor.disabled = false; // 再次启用编辑器
editor.focus(); // 将光标定位到编辑器中
}
}
else { // 离线状态下,不能同步
status("Can't sync while offline");
editor.disabled = false;
editor.focus();
}
}