mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-03 14:59:18 +02:00
added queue for loading and offline storage of parsed info
This commit is contained in:
parent
ca3e2c24c2
commit
d90fd14ad2
12 changed files with 10017 additions and 47 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
BIN
fpjs/.DS_Store
vendored
BIN
fpjs/.DS_Store
vendored
Binary file not shown.
9555
fpjs/libs/jquery-1.9.0.js
vendored
Normal file
9555
fpjs/libs/jquery-1.9.0.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
4
fpjs/libs/jquery-1.9.0.min.js
vendored
Normal file
4
fpjs/libs/jquery-1.9.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -11,16 +11,6 @@ FP.app.init = (function($){
|
|||
//-- Setup the browser prefixes
|
||||
FP.core.crossBrowserColumnCss();
|
||||
|
||||
//-- Temp set the previos position to section 6,
|
||||
// since moby-dick has lots of crap before the test
|
||||
// Might want to make a way to skip to first chapter
|
||||
if (localStorage.getItem("bookURL") === null ||
|
||||
localStorage.getItem("bookURL") != bookURL) {
|
||||
|
||||
localStorage.setItem("bookURL", bookURL);
|
||||
localStorage.setItem("spinePos", 0);
|
||||
}
|
||||
|
||||
//-- Set up our sidebar
|
||||
$("#main").width($(window).width()-40);
|
||||
|
||||
|
@ -150,6 +140,6 @@ FP.app.init = (function($){
|
|||
|
||||
}
|
||||
|
||||
return init;
|
||||
return init;
|
||||
|
||||
})(jQuery);
|
|
@ -17,11 +17,13 @@ FP.Book = function(elem, bookUrl){
|
|||
this.createEvent("book:resized");
|
||||
|
||||
this.initialize(this.el);
|
||||
|
||||
this.online = navigator.onLine;
|
||||
this.listeners();
|
||||
|
||||
//-- Determine storage type
|
||||
// options: none | ram
|
||||
FP.storage.storageMethod("none");
|
||||
FP.storage.storageMethod("ram");
|
||||
|
||||
// BookUrl is optional, but if present start loading process
|
||||
if(bookUrl) {
|
||||
|
@ -31,24 +33,31 @@ FP.Book = function(elem, bookUrl){
|
|||
|
||||
}
|
||||
|
||||
|
||||
//-- Build up any html needed
|
||||
FP.Book.prototype.initialize = function(el){
|
||||
this.iframe = document.createElement('iframe');
|
||||
this.resizeIframe(false, this.el.clientWidth, this.el.clientHeight);
|
||||
|
||||
this.listen("book:resized", this.resizeIframe, this);
|
||||
|
||||
//this.listen("book:bookReady", function(){console.log("rready")});
|
||||
|
||||
|
||||
|
||||
this.el.appendChild(this.iframe);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
FP.Book.prototype.listeners = function(){
|
||||
var that = this;
|
||||
window.addEventListener("resize", that.onResized.bind(this), false);
|
||||
|
||||
window.addEventListener("offline", function(e) {
|
||||
that.online = false;
|
||||
}, false);
|
||||
|
||||
window.addEventListener("online", function(e) {
|
||||
that.online = true;
|
||||
}, false);
|
||||
|
||||
//-- TODO: listener for offline
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,18 +67,61 @@ FP.Book.prototype.loadEpub = function(bookUrl){
|
|||
//-- TODO: Check what storage types are available
|
||||
//-- TODO: Checks if the url is a zip file and unpack
|
||||
if(this.isContained(bookUrl)){
|
||||
console.log("Zipped!");
|
||||
console.error("Zipped!");
|
||||
}
|
||||
|
||||
if(!this.isSaved()){
|
||||
//-- Gets the root of the book and url of the opf
|
||||
this.parseContainer(function(){
|
||||
//-- Gets all setup of the book from xml file
|
||||
//-- TODO: add promise for this instead of callback?
|
||||
this.parseContents();
|
||||
});
|
||||
|
||||
}else{
|
||||
this.tell("book:tocReady");
|
||||
this.tell("book:metadataReady");
|
||||
this.tell("book:spineReady");
|
||||
console.log(this.metadata)
|
||||
//-- Info is saved, start display
|
||||
this.startDisplay();
|
||||
}
|
||||
|
||||
//-- Gets the root of the book and url of the opf
|
||||
this.parseContainer(function(){
|
||||
//-- Gets all setup of the book from xml file
|
||||
//-- TODO: add promise for this instead of callback?
|
||||
this.parseContents();
|
||||
});
|
||||
}
|
||||
|
||||
FP.Book.prototype.isSaved = function(force) {
|
||||
|
||||
if (localStorage.getItem("bookUrl") === null ||
|
||||
localStorage.getItem("bookUrl") != this.bookUrl ||
|
||||
force == true) {
|
||||
|
||||
localStorage.setItem("bookUrl", this.bookUrl);
|
||||
localStorage.setItem("spinePos", 0);
|
||||
localStorage.setItem("stored", 0);
|
||||
|
||||
this.spinePos = 0;
|
||||
this.stored = 0;
|
||||
|
||||
return false;
|
||||
}else{
|
||||
//-- get previous saved positions
|
||||
this.spinePos = parseInt(localStorage.getItem("spinePos")) || 0;
|
||||
this.stored = parseInt(localStorage.getItem("stored")) || 0;
|
||||
|
||||
//-- get previous saved paths
|
||||
this.basePath = localStorage.getItem("basePath");
|
||||
this.contentsPath = localStorage.getItem("contentsPath");
|
||||
|
||||
//-- get previous saved content
|
||||
this.metadata = JSON.parse(localStorage.getItem("metadata"));
|
||||
this.assets = JSON.parse(localStorage.getItem("assets"));
|
||||
this.spine = JSON.parse(localStorage.getItem("spine"));
|
||||
this.toc = JSON.parse(localStorage.getItem("toc"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
FP.Book.prototype.isContained = function(bookUrl){
|
||||
|
@ -128,6 +180,10 @@ FP.Book.prototype.parseContainer = function(callback){
|
|||
|
||||
that.basePath = that.bookUrl + fullpath[0] + "/";
|
||||
that.contentsPath = fullpath[1];
|
||||
|
||||
localStorage.setItem("basePath", that.basePath);
|
||||
localStorage.setItem("contentsPath", that.contentsPath);
|
||||
|
||||
//-- Now that we have the path we can parse the contents
|
||||
//-- TODO: move this
|
||||
that.parseContents(that.contentsPath);
|
||||
|
@ -143,6 +199,7 @@ FP.Book.prototype.parseContents = function(){
|
|||
var metadata = contents.getElementsByTagName("metadata")[0],
|
||||
manifest = contents.getElementsByTagName("manifest")[0],
|
||||
spine = contents.getElementsByTagName("spine")[0];
|
||||
|
||||
that.parseMetadata(metadata);
|
||||
that.parseManifest(manifest);
|
||||
that.parseSpine(spine);
|
||||
|
@ -160,7 +217,9 @@ FP.Book.prototype.parseMetadata = function(metadata){
|
|||
|
||||
this.metadata["bookTitle"] = title ? title.childNodes[0].nodeValue : "";
|
||||
this.metadata["creator"] = creator ? creator.childNodes[0].nodeValue : "";
|
||||
|
||||
|
||||
localStorage.setItem("metadata", JSON.stringify(this.metadata));
|
||||
|
||||
this.tell("book:metadataReady");
|
||||
}
|
||||
|
||||
|
@ -181,6 +240,8 @@ FP.Book.prototype.parseManifest = function(manifest){
|
|||
that.parseTOC(href);
|
||||
}
|
||||
});
|
||||
|
||||
localStorage.setItem("assets", JSON.stringify(this.assets));
|
||||
}
|
||||
|
||||
FP.Book.prototype.parseSpine = function(spine){
|
||||
|
@ -201,6 +262,9 @@ FP.Book.prototype.parseSpine = function(spine){
|
|||
that.spine.push({"id": id, "href": href});
|
||||
that.spineIndex[id] = index;
|
||||
});
|
||||
|
||||
localStorage.setItem("spine", JSON.stringify(this.spine));
|
||||
|
||||
this.tell("book:spineReady");
|
||||
}
|
||||
|
||||
|
@ -252,7 +316,8 @@ FP.Book.prototype.parseTOC = function(path){
|
|||
}
|
||||
|
||||
that.toc = getTOC(navMap.getElementsByTagName("navPoint"), navMap);
|
||||
|
||||
|
||||
localStorage.setItem("toc", JSON.stringify(that.toc));
|
||||
|
||||
that.tell("book:tocReady");
|
||||
/*
|
||||
|
@ -283,16 +348,22 @@ FP.Book.prototype.chapterTitle = function(){
|
|||
}
|
||||
|
||||
FP.Book.prototype.startDisplay = function(){
|
||||
//-- get previous saved positions
|
||||
var spinePos = parseInt(localStorage.getItem("spinePos")) || 0;
|
||||
|
||||
|
||||
|
||||
this.tell("book:bookReady");
|
||||
|
||||
this.displayChapter(spinePos);
|
||||
this.displayChapter(this.spinePos, false, function(){
|
||||
|
||||
//-- What happens if the cache expires / is cleared / changed?
|
||||
//if(!this.stored){
|
||||
this.storeOffline();
|
||||
//}
|
||||
|
||||
}.bind(this));
|
||||
|
||||
|
||||
}
|
||||
|
||||
FP.Book.prototype.displayChapter = function(pos, end){
|
||||
FP.Book.prototype.displayChapter = function(pos, end, callback){
|
||||
var that = this;
|
||||
|
||||
if(pos >= this.spine.length){
|
||||
|
@ -321,7 +392,12 @@ FP.Book.prototype.displayChapter = function(pos, end){
|
|||
|
||||
that.tell("book:chapterReady");
|
||||
|
||||
that.preloadNextChapter();
|
||||
if(callback){
|
||||
callback();
|
||||
}
|
||||
//that.preloadNextChapter();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,10 +441,15 @@ FP.Book.prototype.preloadNextChapter = function(){
|
|||
file = FP.storage.preload(path);
|
||||
}
|
||||
|
||||
FP.Book.prototype.preloadAll = function(){
|
||||
|
||||
FP.Book.prototype.storeOffline = function(callback){
|
||||
var assets = FP.core.toArray(this.assets);
|
||||
FP.storage.batch(assets, function(){
|
||||
this.stored = 1;
|
||||
localStorage.setItem("stored", 1);
|
||||
if(callback) callback();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
FP.Book.prototype.preloadResources = function(){
|
||||
|
||||
FP.Book.prototype.availableOffline = function(){
|
||||
return this.stored > 0 ? true : false;
|
||||
}
|
||||
|
|
|
@ -9,24 +9,34 @@ FP.Chapter = function(book){
|
|||
this.chapterPos = 1;
|
||||
this.leftPos = 0;
|
||||
|
||||
this.loadFromStorage();
|
||||
this.load();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
FP.Chapter.prototype.loadFromStorage = function(){
|
||||
var path = this.path,
|
||||
file = FP.storage.get(path, this.postLoad.bind(this));
|
||||
FP.Chapter.prototype.load = function(){
|
||||
var path = this.path;
|
||||
|
||||
if(this.book.online){
|
||||
this.setIframeSrc(path);
|
||||
}else{
|
||||
this.loadFromStorage(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FP.Chapter.prototype.postLoad = function(file){
|
||||
FP.Chapter.prototype.loadFromStorage = function(path){
|
||||
var file = FP.storage.get(path, this.setIframeSrc.bind(this));
|
||||
}
|
||||
|
||||
FP.Chapter.prototype.setIframeSrc = function(url){
|
||||
var that = this;
|
||||
|
||||
//-- Not sure if this is the best time to do this, but hide current text
|
||||
if(this.bodyEl) this.bodyEl.style.visibility = "hidden";
|
||||
|
||||
this.iframe.src = file;
|
||||
this.iframe.src = url;
|
||||
|
||||
this.iframe.onload = function() {
|
||||
//that.bodyEl = that.iframe.contentDocument.documentElement.getElementsByTagName('body')[0];
|
||||
|
|
|
@ -121,3 +121,18 @@ FP.core.crossBrowserColumnCss = function(){
|
|||
// FP.core.columnWidth = cssIfy(FP.core.columnWidth);
|
||||
|
||||
}
|
||||
|
||||
FP.core.toArray = function(obj) {
|
||||
var arr = [];
|
||||
|
||||
for (member in obj) {
|
||||
var newitm;
|
||||
if ( obj.hasOwnProperty(member) ) {
|
||||
newitm = obj[member];
|
||||
newitm.ident = member;
|
||||
arr.push(newitm);
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
};
|
0
fpjs/render/loader_indexeddb.js
Normal file
0
fpjs/render/loader_indexeddb.js
Normal file
167
fpjs/render/queue.js
Normal file
167
fpjs/render/queue.js
Normal file
|
@ -0,0 +1,167 @@
|
|||
FP.Queue = function(worker, concurrency){
|
||||
this._q = [];
|
||||
this._tasks = {};
|
||||
this.idCount = 0;
|
||||
this.concurrency = 0;
|
||||
|
||||
this.workers = [];
|
||||
this.available = [];
|
||||
|
||||
if(typeof(worker) === "string") {
|
||||
this.workerStr = worker;
|
||||
this.addWorkers(concurrency || 1);
|
||||
}
|
||||
|
||||
if(typeof(worker) === "function") {
|
||||
this.workerFunction = worker;
|
||||
this.addFakeWorkers(concurrency || 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FP.Queue.prototype.addWorkers = function(concurrency){
|
||||
var min = this.concurrency,
|
||||
max = min + concurrency;
|
||||
|
||||
//-- Stop running jobs or something?
|
||||
|
||||
for(var i=min; i < concurrency; i++){
|
||||
var worker = new Worker(this.workerStr);
|
||||
this.workers.push(worker); //-- Add new work
|
||||
this.available.push(i); //-- Make available to start tasks
|
||||
}
|
||||
|
||||
this.concurrency = concurrency;
|
||||
|
||||
}
|
||||
|
||||
FP.Queue.prototype.addFakeWorkers = function(concurrency){
|
||||
var min = this.concurrency,
|
||||
max = min + concurrency;
|
||||
|
||||
//-- Stop running jobs or something?
|
||||
|
||||
for(var i=min; i < concurrency; i++){
|
||||
var worker = new FP.FakeWorker(this.workerFunction);
|
||||
this.workers.push(worker); //-- Add new work
|
||||
this.available.push(i); //-- Make available to start tasks
|
||||
}
|
||||
|
||||
this.concurrency = concurrency;
|
||||
}
|
||||
|
||||
FP.Queue.prototype.add = function(msg, callback, priority){
|
||||
var ID = this.idCount;
|
||||
//-- Add to task object : maybe check for dups
|
||||
this._tasks[ID] = {
|
||||
"msg": msg,
|
||||
"callback": callback || function(){}
|
||||
}
|
||||
|
||||
//-- Add id to queue
|
||||
if(!priority){
|
||||
this._q.push(ID);
|
||||
}else{
|
||||
this._q.unshift(ID);
|
||||
if(!this.running) this.run();
|
||||
}
|
||||
|
||||
//-- Increment ID for next task
|
||||
this.idCount++;
|
||||
|
||||
|
||||
|
||||
return ID;
|
||||
}
|
||||
|
||||
FP.Queue.prototype.addGroup = function(group, callback){
|
||||
var that = this,
|
||||
counter = group.length,
|
||||
after = function(){
|
||||
counter--;
|
||||
if(counter <= 0) callback();
|
||||
};
|
||||
|
||||
group.forEach(function(msg){
|
||||
that.add(msg, after);
|
||||
});
|
||||
|
||||
if(!this.running) this.run();
|
||||
|
||||
return after;
|
||||
}
|
||||
|
||||
FP.Queue.prototype.run = function(id){
|
||||
if(this.running) return;
|
||||
this.running = true;
|
||||
|
||||
while(this.available.length) {
|
||||
var next = this.next();
|
||||
if(!next) break; //-- no tasks left or error
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FP.Queue.prototype.find = function(msg){
|
||||
|
||||
}
|
||||
|
||||
FP.Queue.prototype.next = function(){
|
||||
var that = this,
|
||||
curr = this._q.shift(),
|
||||
task,
|
||||
workerID,
|
||||
worker;
|
||||
|
||||
if(typeof(curr) === "undefined"){
|
||||
//-- Nothing left on queue
|
||||
this.running = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
task = this._tasks[curr];
|
||||
workerID = this.available.pop();
|
||||
worker = this.workers[workerID];
|
||||
|
||||
//-- give worker new task
|
||||
worker.postMessage(task.msg);
|
||||
|
||||
//-- listen for worker response
|
||||
worker.onmessage = function(e){
|
||||
var data = e.data;
|
||||
|
||||
task.callback(data);
|
||||
delete that._tasks[curr]; //-- Remove task
|
||||
|
||||
that.available.push(workerID);
|
||||
that.next();
|
||||
}
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
FP.Queue.prototype.empty = function(){
|
||||
this._q = [];
|
||||
this._tasks = {};
|
||||
//-- TODO: close workers
|
||||
}
|
||||
|
||||
//-- A super simplistic fake worker, is passed a function instead of a script
|
||||
|
||||
FP.FakeWorker = function(func){
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
FP.FakeWorker.prototype.postMessage = function(msg){
|
||||
setTimeout(function(){
|
||||
this.func(msg, this.onmessage);
|
||||
}.bind(this), 1);
|
||||
}
|
||||
|
||||
FP.FakeWorker.prototype.onmessage = function(e){
|
||||
|
||||
}
|
||||
|
||||
FP.FakeWorker.prototype.close = function(e){
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@ FP.storage = function(){
|
|||
|
||||
//-- Handle load errors
|
||||
this._store.failed = _error;
|
||||
|
||||
}
|
||||
|
||||
function get(path, callback) {
|
||||
|
@ -26,6 +27,14 @@ FP.storage = function(){
|
|||
return this._store.preload(path, callback);
|
||||
}
|
||||
|
||||
function batch(group, callback) {
|
||||
return this._store.batch(group, callback);
|
||||
}
|
||||
|
||||
function replace(path, content, callback) {
|
||||
//return this._store.batch(group, callback);
|
||||
}
|
||||
|
||||
function _error(err){
|
||||
console.log("error", err);
|
||||
}
|
||||
|
@ -33,15 +42,18 @@ FP.storage = function(){
|
|||
return {
|
||||
"get" : get,
|
||||
"preload" : preload,
|
||||
"batch" : batch,
|
||||
"storageMethod": storageMethod
|
||||
}
|
||||
|
||||
}();
|
||||
|
||||
|
||||
/*
|
||||
FP.storage.ram = function() {
|
||||
var _store = {},
|
||||
_blobs = {};
|
||||
_blobs = {},
|
||||
_queue = new FP.Queue("loader_ram.js", 3);
|
||||
|
||||
|
||||
//-- TODO: this should be prototypes?
|
||||
|
||||
|
@ -53,6 +65,10 @@ FP.storage.ram = function() {
|
|||
request(path);
|
||||
}
|
||||
}
|
||||
|
||||
function batch(group, callback){
|
||||
_queue.addGroup(group)
|
||||
}
|
||||
|
||||
//-- Fetches url
|
||||
function get(path, callback) {
|
||||
|
@ -148,6 +164,7 @@ FP.storage.ram = function() {
|
|||
"preload" : preload
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
FP.storage.none = function() {
|
||||
var _store = {};
|
||||
|
@ -221,4 +238,133 @@ FP.storage.none = function() {
|
|||
"get" : get,
|
||||
"preload" : preload
|
||||
}
|
||||
}
|
||||
|
||||
FP.storage.ram = function() {
|
||||
var _store = {},
|
||||
_blobs = {},
|
||||
_queue = new FP.Queue(loader, 6);
|
||||
//-- max of 6 concurrent requests: http://www.browserscope.org/?category=network
|
||||
|
||||
|
||||
function loader(msg, callback){
|
||||
var e = {"data":null},
|
||||
fromCache = check(msg);
|
||||
|
||||
if(fromCache){
|
||||
e.data = fromCache;
|
||||
callback(e);
|
||||
}else{
|
||||
request(msg, function(file){
|
||||
e.data = file;
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function preload(path) {
|
||||
var fromCache = check(path);
|
||||
|
||||
if(!fromCache){
|
||||
_queue.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
function batch(group, callback){
|
||||
_queue.addGroup(group, callback);
|
||||
}
|
||||
|
||||
//-- Fetches url
|
||||
function get(path, callback) {
|
||||
var fromCache = check(path),
|
||||
url;
|
||||
|
||||
if(fromCache){
|
||||
url = getURL(path, fromCache);
|
||||
if(typeof(callback) != "undefined"){
|
||||
callback(url);
|
||||
}
|
||||
}else{
|
||||
_queue.add(path, function(file){
|
||||
url = getURL(path, file);
|
||||
if(typeof(callback) != "undefined"){
|
||||
callback(url);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function check(path) {
|
||||
var file = _store[path];
|
||||
|
||||
if(typeof(file) != "undefined"){
|
||||
return file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function request(path, callback) {
|
||||
var xhr = new FP.core.loadFile(path);
|
||||
|
||||
xhr.succeeded = function(file) {
|
||||
//console.log("file", file)
|
||||
cache(path, file);
|
||||
if(typeof(callback) != "undefined"){
|
||||
callback(file);
|
||||
}
|
||||
}
|
||||
|
||||
xhr.failed = _error;
|
||||
|
||||
xhr.start();
|
||||
}
|
||||
|
||||
function cache(path, file) {
|
||||
if(_store[path]) return;
|
||||
|
||||
_store[path] = file;
|
||||
}
|
||||
|
||||
function getURL(path, file){
|
||||
var url;
|
||||
|
||||
if(typeof(_blobs[path]) != "undefined"){
|
||||
return _blobs[path];
|
||||
}
|
||||
|
||||
url = this._URL.createObjectURL(file);
|
||||
|
||||
//-- need to revokeObjectURL previous urls, but only when cleaning cache
|
||||
// this.createdURLs.forEach(function(url){
|
||||
// this._URL.revokeObjectURL(url);
|
||||
// });
|
||||
|
||||
_blobs[path] = url;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
// this.succeeded = function(){
|
||||
// console.log("loaded");
|
||||
// }
|
||||
//
|
||||
// this.failed = function(){
|
||||
// console.log("loaded");
|
||||
// }
|
||||
|
||||
function _error(err){
|
||||
if(typeof(this.failed) == "undefined"){
|
||||
console.log("Error: ", err);
|
||||
}else{
|
||||
this.failed(err);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"get" : get,
|
||||
"preload" : preload,
|
||||
"batch" : batch
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
<link rel="stylesheet" href="css/normalize.css">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
|
||||
<script>window.jQuery || document.write('<script src="fpjs/libs/jquery-1.9.0.min.js"><\/script>')</script>
|
||||
<script src="fpjs/libs/modernizr-2.6.2.min.js"></script>
|
||||
<script>
|
||||
//-- Define Global
|
||||
|
@ -35,6 +36,7 @@
|
|||
|
||||
|
||||
<script async src="fpjs/render/core.js"></script>
|
||||
<script async src="fpjs/render/queue.js"></script>
|
||||
<script async src="fpjs/render/storage.js"></script>
|
||||
<script async src="fpjs/render/events.js"></script>
|
||||
<script async src="fpjs/render/book.js"></script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue