PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/ajax/libs/dojo/1.9.4/back.js.uncompressed.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 397 lines | 205 code | 46 blank | 146 comment | 67 complexity | 6eb3963fe0561d61a6ba8cb5b598d7f9 MD5 | raw file
  1. define("dojo/back", ["./_base/config", "./_base/lang", "./sniff", "./dom", "./dom-construct", "./_base/window", "require"],
  2. function(config, lang, has, dom, domConstruct, baseWindow, require){
  3. // module:
  4. // dojo/back
  5. var back = {
  6. // summary:
  7. // Browser history management resources
  8. };
  9. 1 && lang.setObject("dojo.back", back);
  10. // everyone deals with encoding the hash slightly differently
  11. var getHash = back.getHash = function(){
  12. var h = window.location.hash;
  13. if(h.charAt(0) == "#"){ h = h.substring(1); }
  14. return has("mozilla") ? h : decodeURIComponent(h);
  15. },
  16. setHash = back.setHash = function(h){
  17. if(!h){ h = ""; }
  18. window.location.hash = encodeURIComponent(h);
  19. historyCounter = history.length;
  20. };
  21. var initialHref = (typeof(window) !== "undefined") ? window.location.href : "";
  22. var initialHash = (typeof(window) !== "undefined") ? getHash() : "";
  23. var initialState = null;
  24. var locationTimer = null;
  25. var bookmarkAnchor = null;
  26. var historyIframe = null;
  27. var forwardStack = [];
  28. var historyStack = [];
  29. var moveForward = false;
  30. var changingUrl = false;
  31. var historyCounter;
  32. function handleBackButton(){
  33. // summary:
  34. // private method. Do not call this directly.
  35. //The "current" page is always at the top of the history stack.
  36. var current = historyStack.pop();
  37. if(!current){ return; }
  38. var last = historyStack[historyStack.length-1];
  39. if(!last && historyStack.length == 0){
  40. last = initialState;
  41. }
  42. if(last){
  43. if(last.kwArgs["back"]){
  44. last.kwArgs["back"]();
  45. }else if(last.kwArgs["backButton"]){
  46. last.kwArgs["backButton"]();
  47. }else if(last.kwArgs["handle"]){
  48. last.kwArgs.handle("back");
  49. }
  50. }
  51. forwardStack.push(current);
  52. }
  53. back.goBack = handleBackButton;
  54. function handleForwardButton(){
  55. // summary:
  56. // private method. Do not call this directly.
  57. var last = forwardStack.pop();
  58. if(!last){ return; }
  59. if(last.kwArgs["forward"]){
  60. last.kwArgs.forward();
  61. }else if(last.kwArgs["forwardButton"]){
  62. last.kwArgs.forwardButton();
  63. }else if(last.kwArgs["handle"]){
  64. last.kwArgs.handle("forward");
  65. }
  66. historyStack.push(last);
  67. }
  68. back.goForward = handleForwardButton;
  69. function createState(url, args, hash){
  70. // summary:
  71. // private method. Do not call this directly.
  72. return {"url": url, "kwArgs": args, "urlHash": hash}; //Object
  73. }
  74. function getUrlQuery(url){
  75. // summary:
  76. // private method. Do not call this directly.
  77. var segments = url.split("?");
  78. if(segments.length < 2){
  79. return null; //null
  80. }
  81. else{
  82. return segments[1]; //String
  83. }
  84. }
  85. function loadIframeHistory(){
  86. // summary:
  87. // private method. Do not call this directly.
  88. var url = (config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html")) + "?" + (new Date()).getTime();
  89. moveForward = true;
  90. if(historyIframe){
  91. has("webkit") ? historyIframe.location = url : window.frames[historyIframe.name].location = url;
  92. }else{
  93. //console.warn("dojo/back: Not initialised. You need to call back.init() from a <script> block that lives inside the <body> tag.");
  94. }
  95. return url; //String
  96. }
  97. function checkLocation(){
  98. if(!changingUrl){
  99. var hsl = historyStack.length;
  100. var hash = getHash();
  101. if((hash === initialHash||window.location.href == initialHref)&&(hsl == 1)){
  102. // FIXME: could this ever be a forward button?
  103. // we can't clear it because we still need to check for forwards. Ugg.
  104. // clearInterval(this.locationTimer);
  105. handleBackButton();
  106. return;
  107. }
  108. // first check to see if we could have gone forward. We always halt on
  109. // a no-hash item.
  110. if(forwardStack.length > 0){
  111. if(forwardStack[forwardStack.length-1].urlHash === hash){
  112. handleForwardButton();
  113. return;
  114. }
  115. }
  116. // ok, that didn't work, try someplace back in the history stack
  117. if((hsl >= 2)&&(historyStack[hsl-2])){
  118. if(historyStack[hsl-2].urlHash === hash){
  119. handleBackButton();
  120. }
  121. }
  122. }
  123. }
  124. back.init = function(){
  125. // summary:
  126. // Initializes the undo stack. This must be called from a <script>
  127. // block that lives inside the `<body>` tag to prevent bugs on IE.
  128. //
  129. // Only call this method before the page's DOM is finished loading. Otherwise
  130. // it will not work. Be careful with xdomain loading or djConfig.debugAtAllCosts scenarios,
  131. // in order for this method to work, dojo/back will need to be part of a build layer.
  132. // prevent reinit
  133. if(dom.byId("dj_history")){ return; }
  134. var src = config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html");
  135. if (config.afterOnLoad){
  136. console.error("dojo/back::init() must be called before the DOM has loaded. "
  137. + "Include dojo/back in a build layer.");
  138. }else{
  139. document.write('<iframe style="border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;" name="dj_history" id="dj_history" src="' + src + '"></iframe>');
  140. }
  141. };
  142. back.setInitialState = function(/*Object*/args){
  143. // summary:
  144. // Sets the state object and back callback for the very first page
  145. // that is loaded.
  146. //
  147. // It is recommended that you call this method as part of an event
  148. // listener that is registered via dojo/ready.
  149. // args: Object
  150. // See the addToHistory() function for the list of valid args properties.
  151. initialState = createState(initialHref, args, initialHash);
  152. };
  153. //FIXME: Make these doc comments not be awful. At least they're not wrong.
  154. //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
  155. //FIXME: is there a slight race condition in moz using change URL with the timer check and when
  156. // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
  157. /*=====
  158. var __backArgs = {
  159. // back: Function?
  160. // A function to be called when this state is reached via the user
  161. // clicking the back button.
  162. // forward: Function?
  163. // Upon return to this state from the "back, forward" combination
  164. // of navigation steps, this function will be called. Somewhat
  165. // analogous to the semantic of an "onRedo" event handler.
  166. // changeUrl: Boolean|String?
  167. // Boolean indicating whether or not to create a unique hash for
  168. // this state. If a string is passed instead, it is used as the
  169. // hash.
  170. };
  171. =====*/
  172. back.addToHistory = function(args){
  173. // summary:
  174. // adds a state object (args) to the history list.
  175. // args: __backArgs
  176. // The state object that will be added to the history list.
  177. // description:
  178. // To support getting back button notifications, the object
  179. // argument should implement a function called either "back",
  180. // "backButton", or "handle". The string "back" will be passed as
  181. // the first and only argument to this callback.
  182. //
  183. // To support getting forward button notifications, the object
  184. // argument should implement a function called either "forward",
  185. // "forwardButton", or "handle". The string "forward" will be
  186. // passed as the first and only argument to this callback.
  187. //
  188. // If you want the browser location string to change, define "changeUrl" on the object. If the
  189. // value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
  190. // identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
  191. // not evaluate to false, that value will be used as the fragment identifier. For example,
  192. // if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
  193. //
  194. // There are problems with using dojo/back with semantically-named fragment identifiers
  195. // ("hash values" on an URL). In most browsers it will be hard for dojo/back to know
  196. // distinguish a back from a forward event in those cases. For back/forward support to
  197. // work best, the fragment ID should always be a unique value (something using new Date().getTime()
  198. // for example). If you want to detect hash changes using semantic fragment IDs, then
  199. // consider using dojo/hash instead (in Dojo 1.4+).
  200. //
  201. // example:
  202. // | back.addToHistory({
  203. // | back: function(){ console.log('back pressed'); },
  204. // | forward: function(){ console.log('forward pressed'); },
  205. // | changeUrl: true
  206. // | });
  207. // BROWSER NOTES:
  208. // Safari 1.2:
  209. // back button "works" fine, however it's not possible to actually
  210. // DETECT that you've moved backwards by inspecting window.location.
  211. // Unless there is some other means of locating.
  212. // FIXME: perhaps we can poll on history.length?
  213. // Safari 2.0.3+ (and probably 1.3.2+):
  214. // works fine, except when changeUrl is used. When changeUrl is used,
  215. // Safari jumps all the way back to whatever page was shown before
  216. // the page that uses dojo.undo.browser support.
  217. // IE 5.5 SP2:
  218. // back button behavior is macro. It does not move back to the
  219. // previous hash value, but to the last full page load. This suggests
  220. // that the iframe is the correct way to capture the back button in
  221. // these cases.
  222. // Don't test this page using local disk for MSIE. MSIE will not create
  223. // a history list for iframe_history.html if served from a file: URL.
  224. // The XML served back from the XHR tests will also not be properly
  225. // created if served from local disk. Serve the test pages from a web
  226. // server to test in that browser.
  227. // IE 6.0:
  228. // same behavior as IE 5.5 SP2
  229. // Firefox 1.0+:
  230. // the back button will return us to the previous hash on the same
  231. // page, thereby not requiring an iframe hack, although we do then
  232. // need to run a timer to detect inter-page movement.
  233. //If addToHistory is called, then that means we prune the
  234. //forward stack -- the user went back, then wanted to
  235. //start a new forward path.
  236. forwardStack = [];
  237. var hash = null;
  238. var url = null;
  239. if(!historyIframe){
  240. if(config["useXDomain"] && !config["dojoIframeHistoryUrl"]){
  241. console.warn("dojo/back: When using cross-domain Dojo builds,"
  242. + " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
  243. + " to the path on your domain to iframe_history.html");
  244. }
  245. historyIframe = window.frames["dj_history"];
  246. }
  247. if(!bookmarkAnchor){
  248. bookmarkAnchor = domConstruct.create("a", {style: {display: "none"}}, baseWindow.body());
  249. }
  250. if(args["changeUrl"]){
  251. hash = ""+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
  252. //If the current hash matches the new one, just replace the history object with
  253. //this new one. It doesn't make sense to track different state objects for the same
  254. //logical URL. This matches the browser behavior of only putting in one history
  255. //item no matter how many times you click on the same #hash link, at least in Firefox
  256. //and Safari, and there is no reliable way in those browsers to know if a #hash link
  257. //has been clicked on multiple times. So making this the standard behavior in all browsers
  258. //so that dojo/back's behavior is the same in all browsers.
  259. if(historyStack.length == 0 && initialState.urlHash == hash){
  260. initialState = createState(url, args, hash);
  261. return;
  262. }else if(historyStack.length > 0 && historyStack[historyStack.length - 1].urlHash == hash){
  263. historyStack[historyStack.length - 1] = createState(url, args, hash);
  264. return;
  265. }
  266. changingUrl = true;
  267. setTimeout(function(){
  268. setHash(hash);
  269. changingUrl = false;
  270. }, 1);
  271. bookmarkAnchor.href = hash;
  272. if(has("ie")){
  273. url = loadIframeHistory();
  274. var oldCB = args["back"]||args["backButton"]||args["handle"];
  275. //The function takes handleName as a parameter, in case the
  276. //callback we are overriding was "handle". In that case,
  277. //we will need to pass the handle name to handle.
  278. var tcb = function(handleName){
  279. if(getHash() != ""){
  280. setTimeout(function(){ setHash(hash); }, 1);
  281. }
  282. //Use apply to set "this" to args, and to try to avoid memory leaks.
  283. oldCB.apply(this, [handleName]);
  284. };
  285. //Set interceptor function in the right place.
  286. if(args["back"]){
  287. args.back = tcb;
  288. }else if(args["backButton"]){
  289. args.backButton = tcb;
  290. }else if(args["handle"]){
  291. args.handle = tcb;
  292. }
  293. var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
  294. //The function takes handleName as a parameter, in case the
  295. //callback we are overriding was "handle". In that case,
  296. //we will need to pass the handle name to handle.
  297. var tfw = function(handleName){
  298. if(getHash() != ""){
  299. setHash(hash);
  300. }
  301. if(oldFW){ // we might not actually have one
  302. //Use apply to set "this" to args, and to try to avoid memory leaks.
  303. oldFW.apply(this, [handleName]);
  304. }
  305. };
  306. //Set interceptor function in the right place.
  307. if(args["forward"]){
  308. args.forward = tfw;
  309. }else if(args["forwardButton"]){
  310. args.forwardButton = tfw;
  311. }else if(args["handle"]){
  312. args.handle = tfw;
  313. }
  314. }else if(!has("ie")){
  315. // start the timer
  316. if(!locationTimer){
  317. locationTimer = setInterval(checkLocation, 200);
  318. }
  319. }
  320. }else{
  321. url = loadIframeHistory();
  322. }
  323. historyStack.push(createState(url, args, hash));
  324. };
  325. back._iframeLoaded = function(evt, ifrLoc){
  326. // summary:
  327. // private method. Do not call this directly.
  328. var query = getUrlQuery(ifrLoc.href);
  329. if(query == null){
  330. // alert("iframeLoaded");
  331. // we hit the end of the history, so we should go back
  332. if(historyStack.length == 1){
  333. handleBackButton();
  334. }
  335. return;
  336. }
  337. if(moveForward){
  338. // we were expecting it, so it's not either a forward or backward movement
  339. moveForward = false;
  340. return;
  341. }
  342. //Check the back stack first, since it is more likely.
  343. //Note that only one step back or forward is supported.
  344. if(historyStack.length >= 2 && query == getUrlQuery(historyStack[historyStack.length-2].url)){
  345. handleBackButton();
  346. }else if(forwardStack.length > 0 && query == getUrlQuery(forwardStack[forwardStack.length-1].url)){
  347. handleForwardButton();
  348. }
  349. };
  350. return back;
  351. });