history.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. BrowserHistoryUtils = {
  2. addEvent: function(elm, evType, fn, useCapture) {
  3. useCapture = useCapture || false;
  4. if (elm.addEventListener) {
  5. elm.addEventListener(evType, fn, useCapture);
  6. return true;
  7. }
  8. else if (elm.attachEvent) {
  9. var r = elm.attachEvent('on' + evType, fn);
  10. return r;
  11. }
  12. else {
  13. elm['on' + evType] = fn;
  14. }
  15. }
  16. }
  17. BrowserHistory = (function() {
  18. // type of browser
  19. var browser = {
  20. ie: false,
  21. ie8: false,
  22. firefox: false,
  23. safari: false,
  24. opera: false,
  25. version: -1
  26. };
  27. // Default app state URL to use when no fragment ID present
  28. var defaultHash = '';
  29. // Last-known app state URL
  30. var currentHref = document.location.href;
  31. // Initial URL (used only by IE)
  32. var initialHref = document.location.href;
  33. // Initial URL (used only by IE)
  34. var initialHash = document.location.hash;
  35. // History frame source URL prefix (used only by IE)
  36. var historyFrameSourcePrefix = 'history/historyFrame.html?';
  37. // History maintenance (used only by Safari)
  38. var currentHistoryLength = -1;
  39. // Flag to denote the existence of onhashchange
  40. var browserHasHashChange = false;
  41. var historyHash = [];
  42. var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash);
  43. var backStack = [];
  44. var forwardStack = [];
  45. var currentObjectId = null;
  46. //UserAgent detection
  47. var useragent = navigator.userAgent.toLowerCase();
  48. if (useragent.indexOf("opera") != -1) {
  49. browser.opera = true;
  50. } else if (useragent.indexOf("msie") != -1) {
  51. browser.ie = true;
  52. browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4));
  53. if (browser.version == 8)
  54. {
  55. browser.ie = false;
  56. browser.ie8 = true;
  57. }
  58. } else if (useragent.indexOf("safari") != -1) {
  59. browser.safari = true;
  60. browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7));
  61. } else if (useragent.indexOf("gecko") != -1) {
  62. browser.firefox = true;
  63. }
  64. if (browser.ie == true && browser.version == 7) {
  65. window["_ie_firstload"] = false;
  66. }
  67. function hashChangeHandler()
  68. {
  69. currentHref = document.location.href;
  70. var flexAppUrl = getHash();
  71. //ADR: to fix multiple
  72. if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
  73. var pl = getPlayers();
  74. for (var i = 0; i < pl.length; i++) {
  75. pl[i].browserURLChange(flexAppUrl);
  76. }
  77. } else {
  78. getPlayer().browserURLChange(flexAppUrl);
  79. }
  80. }
  81. // Accessor functions for obtaining specific elements of the page.
  82. function getHistoryFrame()
  83. {
  84. return document.getElementById('ie_historyFrame');
  85. }
  86. function getFormElement()
  87. {
  88. return document.getElementById('safari_formDiv');
  89. }
  90. function getRememberElement()
  91. {
  92. return document.getElementById("safari_remember_field");
  93. }
  94. // Get the Flash player object for performing ExternalInterface callbacks.
  95. // Updated for changes to SWFObject2.
  96. function getPlayer(id) {
  97. var i;
  98. if (id && document.getElementById(id)) {
  99. var r = document.getElementById(id);
  100. if (typeof r.SetVariable != "undefined") {
  101. return r;
  102. }
  103. else {
  104. var o = r.getElementsByTagName("object");
  105. var e = r.getElementsByTagName("embed");
  106. for (i = 0; i < o.length; i++) {
  107. if (typeof o[i].browserURLChange != "undefined")
  108. return o[i];
  109. }
  110. for (i = 0; i < e.length; i++) {
  111. if (typeof e[i].browserURLChange != "undefined")
  112. return e[i];
  113. }
  114. }
  115. }
  116. else {
  117. var o = document.getElementsByTagName("object");
  118. var e = document.getElementsByTagName("embed");
  119. for (i = 0; i < e.length; i++) {
  120. if (typeof e[i].browserURLChange != "undefined")
  121. {
  122. return e[i];
  123. }
  124. }
  125. for (i = 0; i < o.length; i++) {
  126. if (typeof o[i].browserURLChange != "undefined")
  127. {
  128. return o[i];
  129. }
  130. }
  131. }
  132. return undefined;
  133. }
  134. function getPlayers() {
  135. var i;
  136. var players = [];
  137. if (players.length == 0) {
  138. var tmp = document.getElementsByTagName('object');
  139. for (i = 0; i < tmp.length; i++)
  140. {
  141. if (typeof tmp[i].browserURLChange != "undefined")
  142. players.push(tmp[i]);
  143. }
  144. }
  145. if (players.length == 0 || players[0].object == null) {
  146. var tmp = document.getElementsByTagName('embed');
  147. for (i = 0; i < tmp.length; i++)
  148. {
  149. if (typeof tmp[i].browserURLChange != "undefined")
  150. players.push(tmp[i]);
  151. }
  152. }
  153. return players;
  154. }
  155. function getIframeHash() {
  156. var doc = getHistoryFrame().contentWindow.document;
  157. var hash = String(doc.location.search);
  158. if (hash.length == 1 && hash.charAt(0) == "?") {
  159. hash = "";
  160. }
  161. else if (hash.length >= 2 && hash.charAt(0) == "?") {
  162. hash = hash.substring(1);
  163. }
  164. return hash;
  165. }
  166. /* Get the current location hash excluding the '#' symbol. */
  167. function getHash() {
  168. // It would be nice if we could use document.location.hash here,
  169. // but it's faulty sometimes.
  170. var idx = document.location.href.indexOf('#');
  171. return (idx >= 0) ? document.location.href.substr(idx+1) : '';
  172. }
  173. /* Get the current location hash excluding the '#' symbol. */
  174. function setHash(hash) {
  175. // It would be nice if we could use document.location.hash here,
  176. // but it's faulty sometimes.
  177. if (hash == '') hash = '#'
  178. document.location.hash = hash;
  179. }
  180. function createState(baseUrl, newUrl, flexAppUrl) {
  181. return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null };
  182. }
  183. /* Add a history entry to the browser.
  184. * baseUrl: the portion of the location prior to the '#'
  185. * newUrl: the entire new URL, including '#' and following fragment
  186. * flexAppUrl: the portion of the location following the '#' only
  187. */
  188. function addHistoryEntry(baseUrl, newUrl, flexAppUrl) {
  189. //delete all the history entries
  190. forwardStack = [];
  191. if (browser.ie) {
  192. //Check to see if we are being asked to do a navigate for the first
  193. //history entry, and if so ignore, because it's coming from the creation
  194. //of the history iframe
  195. if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) {
  196. currentHref = initialHref;
  197. return;
  198. }
  199. if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) {
  200. newUrl = baseUrl + '#' + defaultHash;
  201. flexAppUrl = defaultHash;
  202. } else {
  203. // for IE, tell the history frame to go somewhere without a '#'
  204. // in order to get this entry into the browser history.
  205. getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl;
  206. }
  207. setHash(flexAppUrl);
  208. } else {
  209. //ADR
  210. if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) {
  211. initialState = createState(baseUrl, newUrl, flexAppUrl);
  212. } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) {
  213. backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl);
  214. }
  215. if (browser.safari && !browserHasHashChange) {
  216. // for Safari, submit a form whose action points to the desired URL
  217. if (browser.version <= 419.3) {
  218. var file = window.location.pathname.toString();
  219. file = file.substring(file.lastIndexOf("/")+1);
  220. getFormElement().innerHTML = '<form name="historyForm" action="'+file+'#' + flexAppUrl + '" method="GET"></form>';
  221. //get the current elements and add them to the form
  222. var qs = window.location.search.substring(1);
  223. var qs_arr = qs.split("&");
  224. for (var i = 0; i < qs_arr.length; i++) {
  225. var tmp = qs_arr[i].split("=");
  226. var elem = document.createElement("input");
  227. elem.type = "hidden";
  228. elem.name = tmp[0];
  229. elem.value = tmp[1];
  230. document.forms.historyForm.appendChild(elem);
  231. }
  232. document.forms.historyForm.submit();
  233. } else {
  234. top.location.hash = flexAppUrl;
  235. }
  236. // We also have to maintain the history by hand for Safari
  237. historyHash[history.length] = flexAppUrl;
  238. _storeStates();
  239. } else {
  240. // Otherwise, just tell the browser to go there
  241. setHash(flexAppUrl);
  242. }
  243. }
  244. backStack.push(createState(baseUrl, newUrl, flexAppUrl));
  245. }
  246. function _storeStates() {
  247. if (browser.safari) {
  248. getRememberElement().value = historyHash.join(",");
  249. }
  250. }
  251. function handleBackButton() {
  252. //The "current" page is always at the top of the history stack.
  253. var current = backStack.pop();
  254. if (!current) { return; }
  255. var last = backStack[backStack.length - 1];
  256. if (!last && backStack.length == 0){
  257. last = initialState;
  258. }
  259. forwardStack.push(current);
  260. }
  261. function handleForwardButton() {
  262. //summary: private method. Do not call this directly.
  263. var last = forwardStack.pop();
  264. if (!last) { return; }
  265. backStack.push(last);
  266. }
  267. function handleArbitraryUrl() {
  268. //delete all the history entries
  269. forwardStack = [];
  270. }
  271. /* Called periodically to poll to see if we need to detect navigation that has occurred */
  272. function checkForUrlChange() {
  273. if (browser.ie) {
  274. if (currentHref != document.location.href && currentHref + '#' != document.location.href) {
  275. //This occurs when the user has navigated to a specific URL
  276. //within the app, and didn't use browser back/forward
  277. //IE seems to have a bug where it stops updating the URL it
  278. //shows the end-user at this point, but programatically it
  279. //appears to be correct. Do a full app reload to get around
  280. //this issue.
  281. if (browser.version < 7) {
  282. currentHref = document.location.href;
  283. document.location.reload();
  284. } else {
  285. if (getHash() != getIframeHash()) {
  286. // this.iframe.src = this.blankURL + hash;
  287. var sourceToSet = historyFrameSourcePrefix + getHash();
  288. getHistoryFrame().src = sourceToSet;
  289. currentHref = document.location.href;
  290. }
  291. }
  292. }
  293. }
  294. if (browser.safari && !browserHasHashChange) {
  295. // For Safari, we have to check to see if history.length changed.
  296. if (currentHistoryLength >= 0 && history.length != currentHistoryLength) {
  297. //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|"));
  298. var flexAppUrl = getHash();
  299. if (browser.version < 528.16 /* Anything earlier than Safari 4.0 */)
  300. {
  301. // If it did change and we're running Safari 3.x or earlier,
  302. // then we have to look the old state up in our hand-maintained
  303. // array since document.location.hash won't have changed,
  304. // then call back into BrowserManager.
  305. currentHistoryLength = history.length;
  306. flexAppUrl = historyHash[currentHistoryLength];
  307. }
  308. //ADR: to fix multiple
  309. if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
  310. var pl = getPlayers();
  311. for (var i = 0; i < pl.length; i++) {
  312. pl[i].browserURLChange(flexAppUrl);
  313. }
  314. } else {
  315. getPlayer().browserURLChange(flexAppUrl);
  316. }
  317. _storeStates();
  318. }
  319. }
  320. if (browser.firefox && !browserHasHashChange) {
  321. if (currentHref != document.location.href) {
  322. var bsl = backStack.length;
  323. var urlActions = {
  324. back: false,
  325. forward: false,
  326. set: false
  327. }
  328. if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) {
  329. urlActions.back = true;
  330. // FIXME: could this ever be a forward button?
  331. // we can't clear it because we still need to check for forwards. Ugg.
  332. // clearInterval(this.locationTimer);
  333. handleBackButton();
  334. }
  335. // first check to see if we could have gone forward. We always halt on
  336. // a no-hash item.
  337. if (forwardStack.length > 0) {
  338. if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) {
  339. urlActions.forward = true;
  340. handleForwardButton();
  341. }
  342. }
  343. // ok, that didn't work, try someplace back in the history stack
  344. if ((bsl >= 2) && (backStack[bsl - 2])) {
  345. if (backStack[bsl - 2].flexAppUrl == getHash()) {
  346. urlActions.back = true;
  347. handleBackButton();
  348. }
  349. }
  350. if (!urlActions.back && !urlActions.forward) {
  351. var foundInStacks = {
  352. back: -1,
  353. forward: -1
  354. }
  355. for (var i = 0; i < backStack.length; i++) {
  356. if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
  357. arbitraryUrl = true;
  358. foundInStacks.back = i;
  359. }
  360. }
  361. for (var i = 0; i < forwardStack.length; i++) {
  362. if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
  363. arbitraryUrl = true;
  364. foundInStacks.forward = i;
  365. }
  366. }
  367. handleArbitraryUrl();
  368. }
  369. // Firefox changed; do a callback into BrowserManager to tell it.
  370. currentHref = document.location.href;
  371. var flexAppUrl = getHash();
  372. //ADR: to fix multiple
  373. if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
  374. var pl = getPlayers();
  375. for (var i = 0; i < pl.length; i++) {
  376. pl[i].browserURLChange(flexAppUrl);
  377. }
  378. } else {
  379. getPlayer().browserURLChange(flexAppUrl);
  380. }
  381. }
  382. }
  383. }
  384. var _initialize = function () {
  385. browserHasHashChange = ("onhashchange" in document.body);
  386. if (browser.ie)
  387. {
  388. var scripts = document.getElementsByTagName('script');
  389. for (var i = 0, s; s = scripts[i]; i++) {
  390. if (s.src.indexOf("history.js") > -1) {
  391. var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html");
  392. }
  393. }
  394. historyFrameSourcePrefix = iframe_location + "?";
  395. var src = historyFrameSourcePrefix;
  396. var iframe = document.createElement("iframe");
  397. iframe.id = 'ie_historyFrame';
  398. iframe.name = 'ie_historyFrame';
  399. iframe.src = 'javascript:false;';
  400. try {
  401. document.body.appendChild(iframe);
  402. } catch(e) {
  403. setTimeout(function() {
  404. document.body.appendChild(iframe);
  405. }, 0);
  406. }
  407. }
  408. if (browser.safari && !browserHasHashChange)
  409. {
  410. var rememberDiv = document.createElement("div");
  411. rememberDiv.id = 'safari_rememberDiv';
  412. document.body.appendChild(rememberDiv);
  413. rememberDiv.innerHTML = '<input type="text" id="safari_remember_field" style="width: 500px;">';
  414. var formDiv = document.createElement("div");
  415. formDiv.id = 'safari_formDiv';
  416. document.body.appendChild(formDiv);
  417. var reloader_content = document.createElement('div');
  418. reloader_content.id = 'safarireloader';
  419. var scripts = document.getElementsByTagName('script');
  420. for (var i = 0, s; s = scripts[i]; i++) {
  421. if (s.src.indexOf("history.js") > -1) {
  422. html = (new String(s.src)).replace(".js", ".html");
  423. }
  424. }
  425. reloader_content.innerHTML = '<iframe id="safarireloader-iframe" src="about:blank" frameborder="no" scrolling="no"></iframe>';
  426. document.body.appendChild(reloader_content);
  427. reloader_content.style.position = 'absolute';
  428. reloader_content.style.left = reloader_content.style.top = '-9999px';
  429. iframe = reloader_content.getElementsByTagName('iframe')[0];
  430. if (document.getElementById("safari_remember_field").value != "" ) {
  431. historyHash = document.getElementById("safari_remember_field").value.split(",");
  432. }
  433. }
  434. if (browserHasHashChange)
  435. document.body.onhashchange = hashChangeHandler;
  436. }
  437. return {
  438. historyHash: historyHash,
  439. backStack: function() { return backStack; },
  440. forwardStack: function() { return forwardStack },
  441. getPlayer: getPlayer,
  442. initialize: function(src) {
  443. _initialize(src);
  444. },
  445. setURL: function(url) {
  446. document.location.href = url;
  447. },
  448. getURL: function() {
  449. return document.location.href;
  450. },
  451. getTitle: function() {
  452. return document.title;
  453. },
  454. setTitle: function(title) {
  455. try {
  456. backStack[backStack.length - 1].title = title;
  457. } catch(e) { }
  458. //if on safari, set the title to be the empty string.
  459. if (browser.safari) {
  460. if (title == "") {
  461. try {
  462. var tmp = window.location.href.toString();
  463. title = tmp.substring((tmp.lastIndexOf("/")+1), tmp.lastIndexOf("#"));
  464. } catch(e) {
  465. title = "";
  466. }
  467. }
  468. }
  469. document.title = title;
  470. },
  471. setDefaultURL: function(def)
  472. {
  473. defaultHash = def;
  474. def = getHash();
  475. //trailing ? is important else an extra frame gets added to the history
  476. //when navigating back to the first page. Alternatively could check
  477. //in history frame navigation to compare # and ?.
  478. if (browser.ie)
  479. {
  480. window['_ie_firstload'] = true;
  481. var sourceToSet = historyFrameSourcePrefix + def;
  482. var func = function() {
  483. getHistoryFrame().src = sourceToSet;
  484. window.location.replace("#" + def);
  485. setInterval(checkForUrlChange, 50);
  486. }
  487. try {
  488. func();
  489. } catch(e) {
  490. window.setTimeout(function() { func(); }, 0);
  491. }
  492. }
  493. if (browser.safari)
  494. {
  495. currentHistoryLength = history.length;
  496. if (historyHash.length == 0) {
  497. historyHash[currentHistoryLength] = def;
  498. var newloc = "#" + def;
  499. window.location.replace(newloc);
  500. } else {
  501. //alert(historyHash[historyHash.length-1]);
  502. }
  503. setInterval(checkForUrlChange, 50);
  504. }
  505. if (browser.firefox || browser.opera)
  506. {
  507. var reg = new RegExp("#" + def + "$");
  508. if (window.location.toString().match(reg)) {
  509. } else {
  510. var newloc ="#" + def;
  511. window.location.replace(newloc);
  512. }
  513. setInterval(checkForUrlChange, 50);
  514. }
  515. },
  516. /* Set the current browser URL; called from inside BrowserManager to propagate
  517. * the application state out to the container.
  518. */
  519. setBrowserURL: function(flexAppUrl, objectId) {
  520. if (browser.ie && typeof objectId != "undefined") {
  521. currentObjectId = objectId;
  522. }
  523. //fromIframe = fromIframe || false;
  524. //fromFlex = fromFlex || false;
  525. //alert("setBrowserURL: " + flexAppUrl);
  526. //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ;
  527. var pos = document.location.href.indexOf('#');
  528. var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href;
  529. var newUrl = baseUrl + '#' + flexAppUrl;
  530. if (document.location.href != newUrl && document.location.href + '#' != newUrl) {
  531. currentHref = newUrl;
  532. addHistoryEntry(baseUrl, newUrl, flexAppUrl);
  533. currentHistoryLength = history.length;
  534. }
  535. },
  536. browserURLChange: function(flexAppUrl) {
  537. var objectId = null;
  538. if (browser.ie && currentObjectId != null) {
  539. objectId = currentObjectId;
  540. }
  541. if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
  542. var pl = getPlayers();
  543. for (var i = 0; i < pl.length; i++) {
  544. try {
  545. pl[i].browserURLChange(flexAppUrl);
  546. } catch(e) { }
  547. }
  548. } else {
  549. try {
  550. getPlayer(objectId).browserURLChange(flexAppUrl);
  551. } catch(e) { }
  552. }
  553. currentObjectId = null;
  554. },
  555. getUserAgent: function() {
  556. return navigator.userAgent;
  557. },
  558. getPlatform: function() {
  559. return navigator.platform;
  560. }
  561. }
  562. })();
  563. // Initialization
  564. // Automated unit testing and other diagnostics
  565. function setURL(url)
  566. {
  567. document.location.href = url;
  568. }
  569. function backButton()
  570. {
  571. history.back();
  572. }
  573. function forwardButton()
  574. {
  575. history.forward();
  576. }
  577. function goForwardOrBackInHistory(step)
  578. {
  579. history.go(step);
  580. }
  581. //BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); });
  582. (function(i) {
  583. var u =navigator.userAgent;var e=/*@cc_on!@*/false;
  584. var st = setTimeout;
  585. if(/webkit/i.test(u)){
  586. st(function(){
  587. var dr=document.readyState;
  588. if(dr=="loaded"||dr=="complete"){i()}
  589. else{st(arguments.callee,10);}},10);
  590. } else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){
  591. document.addEventListener("DOMContentLoaded",i,false);
  592. } else if(e){
  593. (function(){
  594. var t=document.createElement('doc:rdy');
  595. try{t.doScroll('left');
  596. i();t=null;
  597. }catch(e){st(arguments.callee,0);}})();
  598. } else{
  599. window.onload=i;
  600. }
  601. })( function() {BrowserHistory.initialize();} );