1

所以,我正在开发一个 javascript + api 论坛软件。我的观点是,您可以在离线时阅读论坛——这涉及 HTML5 的离线存储。特别是,我想使用 IndexedDB,因为它似乎是未来最有前途的。我已经获得了一个很好的服务/工厂来获取/临时存储数据,但是 IDDB 被严重破坏了。有人对如何解决这个问题有建议吗?

编辑此外,对于任何想要托管版本的人,它位于cloud9上。

var angular = angular || {};
(function(w){
    w.localStorage = w.localStorage||{};
    w.indexedDB = w.indexedDB || w.mozIndexedDB || w.webkitIndexedDB || w.msIndexedDB || null;
    w.IDBTransaction = w.IDBTransaction || w.webkitIDBTransaction || w.msIDBTransaction || null;
    w.IDBKeyRange = w.IDBKeyRange || w.webkitIDBKeyRange || w.msIDBKeyRange || null;
})(this);

angular.module("JSForumServices",[],function($provide){
    $provide.factory('ForumStorage',(function(){
        var service = {
            post:null,
            thread:null,
            board:null,
            cache:{},
            pending:{}
        };
        var fetch = (function(baseFunction,path,callback){
            if(path in service.pending)
                return service.pending[path];
            var r=baseFunction();
            service.pending[path] = r;
            var ajaxRequest = new XMLHttpRequest();
            var cancelled = false;
            var dateRegex =
              /^(?:(Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s+)?(0[1-9]|[1-2]?[0-9]|3[01])\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(19[0-9]{2}|[2-9][0-9]{3})\s+(2[0-3]|[0-1][0-9]):([0-5][0-9])(?::(60|[0-5][0-9]))?\s+([-\+][0-9]{2}[0-5][0-9]|(?:UT|GMT|(?:E|C|M|P)(?:ST|DT)|[A-IK-Z]))(\s+|\(([^\(\)]+|\\\(|\\\))*\))*$/;
            ajaxRequest.onreadystatechange = (function(){
                var readyState = ajaxRequest.readyState;
                if(readyState==4&&(!cancelled)){
                    // Store the copy locally!
                    // Also, initiate the callback.
                    // This way if the storage fails,
                    // The application continues to work
                    // As expected.
                    var data = JSON.parse(ajaxRequest.responseText);
                    for(var k in data)
                        r[k] = data[k];
                    service.cache[path]={obj:r,modified:new Date()};
                    delete service.pending[path];
                    callback(r);
                }
                else if((path in service.cache)&&readyState>=2){
                    var oldDate = service.cache[path].modified;
                    console.log("Cache'd copy for",path,"exists.",oldDate.toString());
                    var isMoreRecent = false;//Is the server-copy more recent?
                    var serverModifiedString = ajaxRequest.getResponseHeader("Last-Modified");
                    console.log(serverModifiedString);
                    var match = dateRegex.exec(serverModifiedString);
                    var serverModified = new Date();
                    serverModified.setDate(parseInt(match[2],10));
                    serverModified.setMonth({
                        "jan":0,
                        "feb":1,
                        "mar":2,
                        "apr":3,
                        "may":4,
                        "jun":5,
                        "jul":6,
                        "aug":7,
                        "sep":8,
                        "oct":9,
                        "nov":10,
                        "dec":11
                    }[match[3].toLowerCase()]);
                    serverModified.setYear(parseInt(match[4],10));
                    serverModified.setHours(parseInt(match[5],10));
                    serverModified.setMinutes(parseInt(match[6],10));
                    serverModified.setMilliseconds(parseInt(match[7],10));
                    isMoreRecent = serverModified > oldDate;
                    if(!isMoreRecent&&(path in service.pending)){//sometimes this code may be slower than network speeds? Just to be safe, I guess.
                        cancelled=true;
                        var oldObject = service.cache[path].obj;
                        for(var key in oldObject)
                            r[key]=oldObject[key];
                        console.log("using a cache'd value.",r);
                        callback(r);
                        delete service.pending[path];
                        ajaxRequest.abort();//No need to waste more bandwidth!
                    }
                }
            });
            ajaxRequest.open("GET",path,true);
            ajaxRequest.send();
            return r;
        });
        var ObjectFetch = (function(base,constr){
            var ret = (function(id,cb){
                cb = cb||(function(){});
                return fetch(constr,base+id+".json",cb);
            });
            return ret;
        });
        service.post = ObjectFetch("./post/",(function(){
            return {
                id:"???",
                author:"???",
                content:"",
                date:""
            };
        }));
        service.thread = ObjectFetch("./thread/",(function(){
            return {
                id:"???",
                title:"???",
                posts:[],
                tags:""
            };
        }));
        service.board = ObjectFetch("./board/",(function(){
            return {
                id:"???",
                title:"???",
                threads:[],
                tags:""
            };
        }));
        service.forum = ObjectFetch("./",(function(){
            return {
                name:"Javascript Forum Software.",
                boards:[]
            };
        }));
        if(window.indexedDB!==null){
            var postFetch = service.post;
            var threadFetch = service.thread;
            var boardFetch = service.board;
            (function(dbengine){
                var boardsLoaded = false;
                var threadsLoaded = false;
                var postsLoaded = false;
                var req = dbengine.open("forum",1);
                req.onupgradeneeded = (function(e){
                   var db = e.target.result;
                   db.createObjectStore("post",{
                       keyPath:"id"
                   });
                   db.createObjectStore("thread",{
                       keyPath:"id"
                   });
                   db.createObjectStore("board",{
                       keyPath:"id"
                   });
                });
                req.onsuccess = (function(e){
                    service.database = e.target.result;
                    var loadData = service.database.transaction([
                        'board',
                        'thread',
                        'post'],'readwrite');
                    loadData.onsuccess = (function(ee){
                       var transaction = ee.target.result;
                       transaction.objectStore("board").openCursor().onsuccess=(function(e){
                           var cursor = e.target.result;
                           if(cursor===null){
                               boardsLoaded = true;
                               return;
                           }
                           var id = cursor.key;
                           var obj = cursor.value;
                           var lastModified = new Date();
                           if("lastModified" in obj){
                               lastModified = obj.lastModified;
                           }
                           service.cache["./board/"+id.toString().toLowerCase()+".json"]={
                               obj:obj,
                               modified:lastModified
                           };
                       });
                       transaction.objectStore("thread").openCursor().onsuccess=(function(e){
                           var cursor = e.target.result;
                           if(cursor===null){
                               threadsLoaded = true;
                               return;
                           }
                           var id = cursor.key;
                           var obj = cursor.value;
                           var lastModified = new Date();
                           if("lastModified" in obj){
                               lastModified = obj.lastModified;
                           }
                           service.cache["./thread/"+id.toString().toLowerCase()+".json"]={
                               obj:obj,
                               modified:lastModified
                           };
                       });
                       transaction.objectStore("post").openCursor().onsuccess=(function(e){
                           var cursor = e.target.result;
                           if(cursor===null){
                               postsLoaded = true;
                               return;
                           }
                           var id = cursor.key;
                           var obj = cursor.value;
                           var lastModified = new Date();
                           if("lastModified" in obj){
                               lastModified = obj.lastModified;
                           }
                           service.cache["./post/"+id.toString().toLowerCase()+".json"]={
                               obj:obj,
                               modified:lastModified
                           };
                       });
                       service.post = (function(id,cb){
                            console.log("DDDDDAFF");
                            var trans = service.database.transaction(["post"],"readwrite");
                            trans.onsuccess = (function(e){
                                var req = e.target.result.objectStore("post").get(id);
                                req.onsuccess = (function(ee){
                                    cb(req.result);
                                });
                                req.onerror = (function(ee){
                                    console.log("HAAAA?!");
                                    postFetch(id,(function(post){
                                        e.target.result.objcetStore.save(post);
                                        cb(post);
                                    }));
                                });
                            });
                            trans.onerror = (function(e){
                                console.log("Error with IDDB:",e);
                                threadFetch(id,cb);
                            });
                        });
                       service.thread = (function(id,cb){
                            var trans = service.database.transaction(["thread"],"readwrite");
                            trans.onsuccess = (function(e){
                                var req = e.target.result.objectStore("thread").get(id);
                                req.onsuccess = (function(ee){
                                    cb(req.result);
                                });
                                req.onerror = (function(ee){
                                    threadFetch(id,(function(post){
                                        e.target.result.objcetStore.save(post);
                                        cb(post);
                                    }));
                                });
                            });
                            trans.onerror = (function(e){
                                console.log("Error with IDDB:",e);
                                postFetch(id,cb);
                            });
                        });
                       service.board = (function(id,cb){
                            var trans = service.database.transaction(["board"],"readwrite");
                            trans.onsuccess = (function(e){
                                var req = e.target.result.objectStore("board").get(id);
                                req.onsuccess = (function(ee){
                                    cb(req.result);
                                });
                                req.onerror = (function(ee){
                                    boardFetch(id,(function(post){
                                        e.target.result.objcetStore.save(post);
                                        cb(post);
                                    }));
                                });
                            });
                            trans.onerror = (function(e){
                                console.log("Error with IDDB:",e);
                                boardFetch(id,cb);
                            });
                        });
                    });
                });
            })(window.indexedDB);
        }
        return service;
    }));
});

angular.module('JSForum',["JSForumServices"]).config(
    ['$routeProvider',
    function($routeProvider){
    $routeProvider.when('/',{
        templateUrl:"forum.html",
        controller:ForumController
    });
    $routeProvider.when('/board/:id',{
        templateUrl:"board.html",
        controller:BoardController
    });
    $routeProvider.when('/thread/:id',{
        templateUrl:"thread.html",
        controller:ThreadController
    });
    $routeProvider.otherwise({redirectTo:"/"});
}]);

function ThreadController($scope,$routeParams,ForumStorage){
    $scope.id = $routeParams.id.toString().toLowerCase();
    $scope.thread = null;
    ForumStorage.thread($scope.id,(function(thread){
        $scope.thread = thread;
        $scope.$apply();
        var callback = (function(p){
            return (function(post){
                $scope.thread.posts[p] = post;
                $scope.$apply();
            });
        });
        for(var i=0;i<thread.posts.length;i++)
            if(typeof(thread.posts[i].id) == 'undefined')
                ForumStorage.post(thread.posts[i],callback(i));
        $scope.$apply();
    }));
}

function BoardController($scope,$routeParams,ForumStorage){
    $scope.id = $routeParams.id.toString().toLowerCase();
    $scope.board = null;
    ForumStorage.board($scope.id,(function(board){
        var callback = (function(p){
            return (function(thread){
                $scope.board.threads[p] = thread;
                $scope.$apply();
            });
        });
        $scope.board = board;
        console.log("Using board:",$scope.board);
        for(var i=0;i<board.threads.length;i++)
            if(typeof(board.threads[i].id)=='undefined')
                ForumStorage.thread(board.threads[i],callback(i));
        $scope.$apply();
    }));
}

function ForumController($scope,ForumStorage){
    $scope.name = localStorage.forumName||"Forum";
    $scope.boards = [];
    ForumStorage.forum("forum",(function(forum){
        document.title = $scope.name = localStorage.forumName = forum.name;
        $scope.boards = forum.boards;
        var callback=(function(p){
            return (function(o){
                $scope.boards[p] = o;
                $scope.$apply();
            });
        });
        for(var i=0;i<$scope.boards.length;i++){
            if(typeof($scope.boards[i].id) == 'undefined')
                ForumStorage.board(forum.boards[i],callback(i));
        }
        $scope.$apply();
    }));
}
4

1 回答 1

-1

如果你的后端是谷歌云存储,你可以使用我的开源数据库库http://dev.yathit.com/api-reference/ydn-db/storage.html它缓存在IndexedDB并持久化到blob存储。论坛帖子 URI 路径作为记录的主键。LastModified标头值被持久化到 IndexedDB 并用于条件 HTTP 请求。由于blob store只能通过key(URI路径)的升序查询,所以会生成论坛帖子的URI路径,使最后一个帖子最小。这样,我们可以通过将最小的已知键作为标记来查询新帖子。

可以在https://bitbucket.org/ytkyaw/ydn-auth/src/master/examples/note-app/note-app.js中找到一个示例(不是论坛,而是一个简单的笔记应用程序)。您需要一个生成签名 url以发布新记录的后端服务器。

于 2013-05-12T08:31:36.127 回答