b3da124f8fb9ab84ba4549704e213523ddcf1eb9.svn-base 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. if (!dojo._hasResource["dojox.off._common"]) { // _hasResource checks added by
  2. // build. Do not use
  3. // _hasResource directly in your
  4. // code.
  5. dojo._hasResource["dojox.off._common"] = true;
  6. dojo.provide("dojox.off._common");
  7. dojo.require("dojox.storage");
  8. dojo.require("dojox.sql");
  9. dojo.require("dojox.off.sync");
  10. // Author: Brad Neuberg, bkn3@columbia.edu, http://codinginparadise.org
  11. // summary:
  12. // dojox.off is the main object for offline applications.
  13. dojo.mixin(dojox.off, {
  14. // isOnline: boolean
  15. // true if we are online, false if not
  16. isOnline : false,
  17. // NET_CHECK: int
  18. // For advanced usage; most developers can ignore this.
  19. // Time in seconds on how often we should check the status of the
  20. // network with an automatic background timer. The current default
  21. // is 5 seconds.
  22. NET_CHECK : 5,
  23. // STORAGE_NAMESPACE: String
  24. // For advanced usage; most developers can ignore this.
  25. // The namespace we use to save core data into Dojo Storage.
  26. STORAGE_NAMESPACE : "_dot",
  27. // enabled: boolean
  28. // For advanced usage; most developers can ignore this.
  29. // Whether offline ability is enabled or not. Defaults to true.
  30. enabled : true,
  31. // availabilityURL: String
  32. // For advanced usage; most developers can ignore this.
  33. // The URL to check for site availability. We do a GET request on
  34. // this URL to check for site availability. By default we check for a
  35. // simple text file in src/off/network_check.txt that has one value
  36. // it, the value '1'.
  37. availabilityURL : dojo.moduleUrl("dojox", "off/network_check.txt"),
  38. // goingOnline: boolean
  39. // For advanced usage; most developers can ignore this.
  40. // True if we are attempting to go online, false otherwise
  41. goingOnline : false,
  42. // coreOpFailed: boolean
  43. // For advanced usage; most developers can ignore this.
  44. // A flag set by the Dojo Offline framework that indicates that the
  45. // user denied some operation that required the offline cache or an
  46. // operation failed in some critical way that was unrecoverable. For
  47. // example, if the offline cache is Google Gears and we try to get a
  48. // Gears database, a popup window appears asking the user whether they
  49. // will approve or deny this request. If the user denies the request,
  50. // and we are doing some operation that is core to Dojo Offline, then
  51. // we set this flag to 'true'. This flag causes a 'fail fast'
  52. // condition, turning off offline ability.
  53. coreOpFailed : false,
  54. // doNetChecking: boolean
  55. // For advanced usage; most developers can ignore this.
  56. // Whether to have a timing interval in the background doing automatic
  57. // network checks at regular intervals; the length of time between
  58. // checks is controlled by dojox.off.NET_CHECK. Defaults to true.
  59. doNetChecking : true,
  60. // hasOfflineCache: boolean
  61. // For advanced usage; most developers can ignore this.
  62. // Determines if an offline cache is available or installed; an
  63. // offline cache is a facility that can truely cache offline
  64. // resources, such as JavaScript, HTML, etc. in such a way that they
  65. // won't be removed from the cache inappropriately like a browser
  66. // cache would. If this is false then an offline cache will be
  67. // installed. Only Google Gears is currently supported as an offline
  68. // cache. Future possible offline caches include Firefox 3.
  69. hasOfflineCache : null,
  70. // browserRestart: boolean
  71. // For advanced usage; most developers can ignore this.
  72. // If true, the browser must be restarted to register the existence of
  73. // a new host added offline (from a call to addHostOffline); if false,
  74. // then nothing is needed.
  75. browserRestart : false,
  76. _STORAGE_APP_NAME : window.location.href.replace(/[^0-9A-Za-z_]/g, "_"),
  77. _initializeCalled : false,
  78. _storageLoaded : false,
  79. _pageLoaded : false,
  80. onLoad : function() {
  81. // summary:
  82. // Called when Dojo Offline can be used.
  83. // description:
  84. // Do a dojo.connect to this to know when you can
  85. // start using Dojo Offline:
  86. // dojo.connect(dojox.off, "onLoad", myFunc);
  87. },
  88. onNetwork : function(type) {
  89. // summary:
  90. // Called when our on- or offline- status changes.
  91. // description:
  92. // If we move online, then this method is called with the
  93. // value "online". If we move offline, then this method is
  94. // called with the value "offline". You can connect to this
  95. // method to do add your own behavior:
  96. //
  97. // dojo.connect(dojox.off, "onNetwork", someFunc)
  98. //
  99. // Note that if you are using the default Dojo Offline UI
  100. // widget that most of the on- and off-line notification
  101. // and syncing is automatically handled and provided to the
  102. // user.
  103. // type: String
  104. // Either "online" or "offline".
  105. },
  106. initialize : function() { /* void */
  107. // summary:
  108. // Called when a Dojo Offline-enabled application is finished
  109. // configuring Dojo Offline, and is ready for Dojo Offline to
  110. // initialize itself.
  111. // description:
  112. // When an application has finished filling out the variables Dojo
  113. // Offline needs to work, such as dojox.off.ui.appName, it must
  114. // this method to tell Dojo Offline to initialize itself.
  115. // Note:
  116. // This method is needed for a rare edge case. In some conditions,
  117. // especially if we are dealing with a compressed Dojo build, the
  118. // entire Dojo Offline subsystem might initialize itself and be
  119. // running even before the JavaScript for an application has had a
  120. // chance to run and configure Dojo Offline, causing Dojo Offline
  121. // to have incorrect initialization parameters for a given app,
  122. // such as no value for dojox.off.ui.appName. This method is
  123. // provided to prevent this scenario, to slightly 'slow down' Dojo
  124. // Offline so it can be configured before running off and doing
  125. // its thing.
  126. // console.debug("dojox.off.initialize");
  127. this._initializeCalled = true;
  128. if (this._storageLoaded && this._pageLoaded) {
  129. this._onLoad();
  130. }
  131. },
  132. goOffline : function() { /* void */
  133. // summary:
  134. // For advanced usage; most developers can ignore this.
  135. // Manually goes offline, away from the network.
  136. if ((dojox.off.sync.isSyncing) || (this.goingOnline)) {
  137. return;
  138. }
  139. this.goingOnline = false;
  140. this.isOnline = false;
  141. },
  142. goOnline : function(callback) { /* void */
  143. // summary:
  144. // For advanced usage; most developers can ignore this.
  145. // Attempts to go online.
  146. // description:
  147. // Attempts to go online, making sure this web application's web
  148. // site is available. 'callback' is called asychronously with the
  149. // result of whether we were able to go online or not.
  150. // callback: Function
  151. // An optional callback function that will receive one argument:
  152. // whether the site is available or not and is boolean. If this
  153. // function is not present we call dojo.xoff.onOnline instead if
  154. // we are able to go online.
  155. // console.debug("goOnline");
  156. if (dojox.off.sync.isSyncing || dojox.off.goingOnline) {
  157. return;
  158. }
  159. this.goingOnline = true;
  160. this.isOnline = false;
  161. // see if can reach our web application's web site
  162. this._isSiteAvailable(callback);
  163. },
  164. onFrameworkEvent : function(type /* String */, saveData /* Object? */) {
  165. // summary:
  166. // For advanced usage; most developers can ignore this.
  167. // A standard event handler that can be attached to to find out
  168. // about low-level framework events. Most developers will not need
  169. // to
  170. // attach to this method; it is meant for low-level information
  171. // that can be useful for updating offline user-interfaces in
  172. // exceptional circumstances. The default Dojo Offline UI
  173. // widget takes care of most of these situations.
  174. // type: String
  175. // The type of the event:
  176. //
  177. // * "offlineCacheInstalled"
  178. // An event that is fired when a user
  179. // has installed an offline cache after the page has been loaded.
  180. // If a user didn't have an offline cache when the page loaded, a
  181. // UI of some kind might have prompted them to download one. This
  182. // method is called if they have downloaded and installed an
  183. // offline cache so a UI can reinitialize itself to begin using
  184. // this offline cache.
  185. // * "coreOperationFailed"
  186. // Fired when a core operation during interaction with the
  187. // offline cache is denied by the user. Some offline caches, such
  188. // as Google Gears, prompts the user to approve or deny caching
  189. // files, using the database, and more. If the user denies a
  190. // request that is core to Dojo Offline's operation, we set
  191. // dojox.off.coreOpFailed to true and call this method for
  192. // listeners that would like to respond some how to Dojo Offline
  193. // 'failing fast'.
  194. // * "save"
  195. // Called whenever the framework saves data into persistent
  196. // storage. This could be useful for providing save feedback
  197. // or providing appropriate error feedback if saving fails
  198. // due to a user not allowing the save to occur
  199. // saveData: Object?
  200. // If the type was 'save', then a saveData object is provided with
  201. // further save information. This object has the following
  202. // properties:
  203. //
  204. // * status - dojox.storage.SUCCESS, dojox.storage.PENDING,
  205. // dojox.storage.FAILED
  206. // Whether the save succeeded, whether it is pending based on a UI
  207. // dialog asking the user for permission, or whether it failed.
  208. //
  209. // * isCoreSave - boolean
  210. // If true, then this save was for a core piece of data necessary
  211. // for the functioning of Dojo Offline. If false, then it is a
  212. // piece of normal data being saved for offline access. Dojo
  213. // Offline will 'fail fast' if some core piece of data could not
  214. // be saved, automatically setting dojox.off.coreOpFailed to
  215. // 'true' and dojox.off.enabled to 'false'.
  216. //
  217. // * key - String
  218. // The key that we are attempting to persist
  219. //
  220. // * value - Object
  221. // The object we are trying to persist
  222. //
  223. // * namespace - String
  224. // The Dojo Storage namespace we are saving this key/value pair
  225. // into, such as "default", "Documents", "Contacts", etc.
  226. // Optional.
  227. if (type == "save") {
  228. if (saveData.isCoreSave
  229. && (saveData.status == dojox.storage.FAILED)) {
  230. dojox.off.coreOpFailed = true;
  231. dojox.off.enabled = false;
  232. // FIXME: Stop the background network thread
  233. dojox.off.onFrameworkEvent("coreOperationFailed");
  234. }
  235. } else if (type == "coreOperationFailed") {
  236. dojox.off.coreOpFailed = true;
  237. dojox.off.enabled = false;
  238. // FIXME: Stop the background network thread
  239. }
  240. },
  241. _checkOfflineCacheAvailable : function(callback) {
  242. // is a true, offline cache running on this machine?
  243. this.hasOfflineCache = dojo.isGears;
  244. callback();
  245. },
  246. _onLoad : function() {
  247. // console.debug("dojox.off._onLoad");
  248. // both local storage and the page are finished loading
  249. // cache the Dojo JavaScript -- just use the default dojo.js
  250. // name for the most common scenario
  251. // FIXME: TEST: Make sure syncing doesn't break if dojo.js
  252. // can't be found, or report an error to developer
  253. dojox.off.files.cache(dojo.moduleUrl("dojo", "dojo.js"));
  254. // pull in the files needed by Dojo
  255. this._cacheDojoResources();
  256. // FIXME: need to pull in the firebug lite files here!
  257. // workaround or else we will get an error on page load
  258. // from Dojo that it can't find 'console.debug' for optimized builds
  259. // dojox.off.files.cache(djConfig.baseRelativePath +
  260. // "src/debug.js");
  261. // make sure that resources needed by all of our underlying
  262. // Dojo Storage storage providers will be available
  263. // offline
  264. dojox.off.files.cache(dojox.storage.manager.getResourceList());
  265. // slurp the page if the end-developer wants that
  266. dojox.off.files._slurp();
  267. // see if we have an offline cache; when done, move
  268. // on to the rest of our startup tasks
  269. this._checkOfflineCacheAvailable(dojo.hitch(this,
  270. "_onOfflineCacheChecked"));
  271. },
  272. _onOfflineCacheChecked : function() {
  273. // this method is part of our _onLoad series of startup tasks
  274. // if we have an offline cache, see if we have been added to the
  275. // list of available offline web apps yet
  276. if (this.hasOfflineCache && this.enabled) {
  277. // load framework data; when we are finished, continue
  278. // initializing ourselves
  279. this._load(dojo.hitch(this, "_finishStartingUp"));
  280. } else if (this.hasOfflineCache && !this.enabled) {
  281. // we have an offline cache, but it is disabled for some reason
  282. // perhaps due to the user denying a core operation
  283. this._finishStartingUp();
  284. } else {
  285. this._keepCheckingUntilInstalled();
  286. }
  287. },
  288. _keepCheckingUntilInstalled : function() {
  289. // this method is part of our _onLoad series of startup tasks
  290. // kick off a background interval that keeps
  291. // checking to see if an offline cache has been
  292. // installed since this page loaded
  293. // FIXME: Gears: See if we are installed somehow after the
  294. // page has been loaded
  295. // now continue starting up
  296. this._finishStartingUp();
  297. },
  298. _finishStartingUp : function() {
  299. // console.debug("dojox.off._finishStartingUp");
  300. // this method is part of our _onLoad series of startup tasks
  301. if (!this.hasOfflineCache) {
  302. this.onLoad();
  303. } else if (this.enabled) {
  304. // kick off a thread to check network status on
  305. // a regular basis
  306. this._startNetworkThread();
  307. // try to go online
  308. this.goOnline(dojo.hitch(this, function() {
  309. // console.debug("Finished trying to go online");
  310. // indicate we are ready to be used
  311. dojox.off.onLoad();
  312. }));
  313. } else { // we are disabled or a core operation failed
  314. if (this.coreOpFailed) {
  315. this.onFrameworkEvent("coreOperationFailed");
  316. } else {
  317. this.onLoad();
  318. }
  319. }
  320. },
  321. _onPageLoad : function() {
  322. // console.debug("dojox.off._onPageLoad");
  323. this._pageLoaded = true;
  324. if (this._storageLoaded && this._initializeCalled) {
  325. this._onLoad();
  326. }
  327. },
  328. _onStorageLoad : function() {
  329. // console.debug("dojox.off._onStorageLoad");
  330. this._storageLoaded = true;
  331. // were we able to initialize storage? if
  332. // not, then this is a core operation, and
  333. // let's indicate we will need to fail fast
  334. if (!dojox.storage.manager.isAvailable()
  335. && dojox.storage.manager.isInitialized()) {
  336. this.coreOpFailed = true;
  337. this.enabled = false;
  338. }
  339. if (this._pageLoaded && this._initializeCalled) {
  340. this._onLoad();
  341. }
  342. },
  343. _isSiteAvailable : function(callback) {
  344. // summary:
  345. // Determines if our web application's website is available.
  346. // description:
  347. // This method will asychronously determine if our web
  348. // application's web site is available, which is a good proxy for
  349. // network availability. The URL dojox.off.availabilityURL is
  350. // used, which defaults to this site's domain name (ex:
  351. // foobar.com). We check for dojox.off.AVAILABILITY_TIMEOUT (in
  352. // seconds) and abort after that
  353. // callback: Function
  354. // An optional callback function that will receive one argument:
  355. // whether the site is available or not and is boolean. If this
  356. // function is not present we call dojox.off.onNetwork instead if we
  357. // are able to go online.
  358. dojo.xhrGet({
  359. url : this._getAvailabilityURL(),
  360. handleAs : "text",
  361. timeout : this.NET_CHECK * 1000,
  362. error : dojo.hitch(this, function(err) {
  363. // console.debug("dojox.off._isSiteAvailable.error:
  364. // " + err);
  365. this.goingOnline = false;
  366. this.isOnline = false;
  367. if (callback) {
  368. callback(false);
  369. }
  370. }),
  371. load : dojo.hitch(this, function(data) {
  372. // console.debug("dojox.off._isSiteAvailable.load,
  373. // data="+data);
  374. this.goingOnline = false;
  375. this.isOnline = true;
  376. if (callback) {
  377. callback(true);
  378. } else {
  379. this.onNetwork("online");
  380. }
  381. })
  382. });
  383. },
  384. _startNetworkThread : function() {
  385. // console.debug("startNetworkThread");
  386. // kick off a thread that does periodic
  387. // checks on the status of the network
  388. if (!this.doNetChecking) {
  389. return;
  390. }
  391. window.setInterval(dojo.hitch(this, function() {
  392. var d = dojo.xhrGet({
  393. url : this._getAvailabilityURL(),
  394. handleAs : "text",
  395. timeout : this.NET_CHECK * 1000,
  396. error : dojo.hitch(this, function(err) {
  397. if (this.isOnline) {
  398. this.isOnline = false;
  399. // FIXME: xhrGet() is not
  400. // correctly calling abort
  401. // on the XHR object when
  402. // it times out; fix inside
  403. // there instead of externally
  404. // here
  405. try {
  406. if (typeof d.ioArgs.xhr.abort == "function") {
  407. d.ioArgs.xhr.abort();
  408. }
  409. } catch (e) {
  410. }
  411. // if things fell in the middle of syncing,
  412. // stop syncing
  413. dojox.off.sync.isSyncing = false;
  414. this.onNetwork("offline");
  415. }
  416. }),
  417. load : dojo.hitch(this, function(data) {
  418. if (!this.isOnline) {
  419. this.isOnline = true;
  420. this.onNetwork("online");
  421. }
  422. })
  423. });
  424. }), this.NET_CHECK * 1000);
  425. },
  426. _getAvailabilityURL : function() {
  427. var url = this.availabilityURL.toString();
  428. // bust the browser's cache to make sure we are really talking to
  429. // the server
  430. if (url.indexOf("?") == -1) {
  431. url += "?";
  432. } else {
  433. url += "&";
  434. }
  435. url += "browserbust=" + allGetServerTime().getTime();
  436. return url;
  437. },
  438. _onOfflineCacheInstalled : function() {
  439. this.onFrameworkEvent("offlineCacheInstalled");
  440. },
  441. _cacheDojoResources : function() {
  442. // if we are a non-optimized build, then the core Dojo bootstrap
  443. // system was loaded as separate JavaScript files;
  444. // add these to our offline cache list. these are
  445. // loaded before the dojo.require() system exists
  446. // FIXME: create a better mechanism in the Dojo core to
  447. // expose whether you are dealing with an optimized build;
  448. // right now we just scan the SCRIPT tags attached to this
  449. // page and see if there is one for _base/_loader/bootstrap.js
  450. var isOptimizedBuild = true;
  451. dojo.forEach(dojo.query("script"), function(i) {
  452. var src = i.getAttribute("src");
  453. if (!src) {
  454. return;
  455. }
  456. if (src.indexOf("_base/_loader/bootstrap.js") != -1) {
  457. isOptimizedBuild = false;
  458. }
  459. });
  460. if (!isOptimizedBuild) {
  461. dojox.off.files.cache(dojo.moduleUrl("dojo", "_base.js").uri);
  462. dojox.off.files.cache(dojo.moduleUrl("dojo",
  463. "_base/_loader/loader.js").uri);
  464. dojox.off.files.cache(dojo.moduleUrl("dojo",
  465. "_base/_loader/bootstrap.js").uri);
  466. // FIXME: pull in the host environment file in a more generic
  467. // way
  468. // for other host environments
  469. dojox.off.files.cache(dojo.moduleUrl("dojo",
  470. "_base/_loader/hostenv_browser.js").uri);
  471. }
  472. // add anything that was brought in with a
  473. // dojo.require() that resulted in a JavaScript
  474. // URL being fetched
  475. // FIXME: modify dojo/_base/_loader/loader.js to
  476. // expose a public API to get this information
  477. for (var i = 0; i < dojo._loadedUrls.length; i++) {
  478. dojox.off.files.cache(dojo._loadedUrls[i]);
  479. }
  480. // FIXME: add the standard Dojo CSS file
  481. },
  482. _save : function() {
  483. // summary:
  484. // Causes the Dojo Offline framework to save its configuration
  485. // data into local storage.
  486. },
  487. _load : function(callback) {
  488. // summary:
  489. // Causes the Dojo Offline framework to load its configuration
  490. // data from local storage
  491. dojox.off.sync._load(callback);
  492. }
  493. });
  494. // wait until the storage system is finished loading
  495. dojox.storage.manager.addOnLoad(dojo.hitch(dojox.off, "_onStorageLoad"));
  496. // wait until the page is finished loading
  497. dojo.addOnLoad(dojox.off, "_onPageLoad");
  498. }