[ Index ]
 

Code source de Seagull 0.6.1

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/js/html_ajax/ -> dhtmlHistory.js (source)

   1  /** 

   2  Copyright (c) 2005, Brad Neuberg, bkn3@columbia.edu

   3  http://codinginparadise.org

   4  

   5  Permission is hereby granted, free of charge, to any person obtaining 

   6  a copy of this software and associated documentation files (the "Software"), 

   7  to deal in the Software without restriction, including without limitation 

   8  the rights to use, copy, modify, merge, publish, distribute, sublicense, 

   9  and/or sell copies of the Software, and to permit persons to whom the 

  10  Software is furnished to do so, subject to the following conditions:

  11  

  12  The above copyright notice and this permission notice shall be 

  13  included in all copies or substantial portions of the Software.

  14  

  15  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 

  16  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 

  17  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 

  18  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 

  19  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 

  20  OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 

  21  THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  22  */
  23  
  24  /** An object that provides DHTML history, history data, and bookmarking 

  25      for AJAX applications. */
  26  window.dhtmlHistory = {
  27  /** Initializes our DHTML history. You should

  28      call this after the page is finished loading. */
  29  /** public */ initialize: function() {
  30      // only Internet Explorer needs to be explicitly initialized;

  31      // other browsers don't have its particular behaviors.

  32      // Basicly, IE doesn't autofill form data until the page

  33      // is finished loading, which means historyStorage won't

  34      // work until onload has been fired.

  35      if (this.isInternetExplorer() == false) {
  36          return;
  37      }
  38          
  39      // if this is the first time this page has loaded...

  40      if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
  41          this.fireOnNewListener = false;
  42          this.firstLoad = true;
  43          historyStorage.put("DhtmlHistory_pageLoaded", true);
  44      }
  45      // else if this is a fake onload event

  46      else {
  47          this.fireOnNewListener = true;
  48          this.firstLoad = false;
  49      }
  50  },
  51              
  52  /** Adds a history change listener. Note that

  53      only one listener is supported at this

  54      time. */
  55  /** public */ addListener: function(callback) {
  56      this.listener = callback;
  57      
  58      // if the page was just loaded and we

  59      // should not ignore it, fire an event

  60      // to our new listener now

  61      if (this.fireOnNewListener == true) {
  62          this.fireHistoryEvent(this.currentLocation);
  63          this.fireOnNewListener = false;
  64      }
  65  },
  66  
  67  /** public */ add: function(newLocation, historyData) {
  68      // most browsers require that we wait a certain amount of time before changing the

  69      // location, such as 200 milliseconds; rather than forcing external callers to use

  70      // window.setTimeout to account for this to prevent bugs, we internally handle this

  71      // detail by using a 'currentWaitTime' variable and have requests wait in line

  72      var self = this;
  73      var addImpl = function() {
  74          // indicate that the current wait time is now less

  75          if (self.currentWaitTime > 0)
  76              self.currentWaitTime = self.currentWaitTime - self.WAIT_TIME;
  77              
  78          // remove any leading hash symbols on newLocation

  79          newLocation = self.removeHash(newLocation);
  80          
  81          // IE has a strange bug; if the newLocation

  82          // is the same as _any_ preexisting id in the

  83          // document, then the history action gets recorded

  84          // twice; throw a programmer exception if there is

  85          // an element with this ID

  86          var idCheck = document.getElementById(newLocation);
  87          if (idCheck != undefined || idCheck != null) {
  88              var message = 
  89              "Exception: History locations can not have "
  90              + "the same value as _any_ id's "
  91              + "that might be in the document, "
  92              + "due to a bug in Internet "
  93              + "Explorer; please ask the "
  94              + "developer to choose a history "
  95              + "location that does not match "
  96              + "any HTML id's in this "
  97              + "document. The following ID "
  98              + "is already taken and can not "
  99              + "be a location: " 
 100              + newLocation;
 101              
 102              throw message; 
 103          }
 104          
 105          // store the history data into history storage

 106          historyStorage.put(newLocation, historyData);
 107          
 108          // indicate to the browser to ignore this upcomming 

 109          // location change

 110          self.ignoreLocationChange = true;
 111   
 112          // indicate to IE that this is an atomic location change

 113          // block

 114          this.ieAtomicLocationChange = true;
 115                  
 116          // save this as our current location

 117          self.currentLocation = newLocation;
 118          
 119          // change the browser location

 120          window.location.hash = newLocation;
 121          
 122          // change the hidden iframe's location if on IE

 123          if (self.isInternetExplorer())
 124              self.iframe.src = "blank.html?" + newLocation;
 125              
 126          // end of atomic location change block

 127          // for IE

 128          this.ieAtomicLocationChange = false;
 129      };
 130  
 131      // now execute this add request after waiting a certain amount of time, so as to

 132      // queue up requests

 133      window.setTimeout(addImpl, this.currentWaitTime);
 134  
 135      // indicate that the next request will have to wait for awhile

 136      this.currentWaitTime = this.currentWaitTime + this.WAIT_TIME;
 137  },
 138  
 139  /** public */ isFirstLoad: function() {
 140      if (this.firstLoad == true) {
 141          return true;
 142      }
 143      else {
 144          return false;
 145      }
 146  },
 147  
 148  /** public */ isInternational: function() {
 149      return false;
 150  },
 151  
 152  /** public */ getVersion: function() {
 153      return "0.05";
 154  },
 155  
 156  /** Gets the current hash value that is in the browser's

 157      location bar, removing leading # symbols if they are present. */
 158  /** public */ getCurrentLocation: function() {
 159      var currentLocation = this.removeHash(window.location.hash);
 160          
 161      return currentLocation;
 162  },
 163  
 164  
 165  
 166  
 167  
 168  /** Our current hash location, without the "#" symbol. */

 169  /** private */ currentLocation: null,
 170  
 171  /** Our history change listener. */

 172  /** private */ listener: null,
 173  
 174  /** A hidden IFrame we use in Internet Explorer to detect history

 175      changes. */
 176  /** private */ iframe: null,
 177  
 178  /** Indicates to the browser whether to ignore location changes. */

 179  /** private */ ignoreLocationChange: null,
 180   
 181  /** The amount of time in milliseconds that we should wait between add requests. 

 182      Firefox is okay with 200 ms, but Internet Explorer needs 400. */
 183  /** private */ WAIT_TIME: 200,
 184  
 185  /** The amount of time in milliseconds an add request has to wait in line before being

 186      run on a window.setTimeout. */
 187  /** private */ currentWaitTime: 0,
 188  
 189  /** A flag that indicates that we should fire a history change event

 190      when we are ready, i.e. after we are initialized and

 191      we have a history change listener. This is needed due to 

 192      an edge case in browsers other than Internet Explorer; if

 193      you leave a page entirely then return, we must fire this

 194      as a history change event. Unfortunately, we have lost

 195      all references to listeners from earlier, because JavaScript

 196      clears out. */
 197  /** private */ fireOnNewListener: null,
 198  
 199  /** A variable that indicates whether this is the first time

 200      this page has been loaded. If you go to a web page, leave

 201      it for another one, and then return, the page's onload

 202      listener fires again. We need a way to differentiate

 203      between the first page load and subsequent ones.

 204      This variable works hand in hand with the pageLoaded

 205      variable we store into historyStorage.*/
 206  /** private */ firstLoad: null,
 207  
 208  /** A variable to handle an important edge case in Internet

 209      Explorer. In IE, if a user manually types an address into

 210      their browser's location bar, we must intercept this by

 211      continiously checking the location bar with an timer 

 212      interval. However, if we manually change the location

 213      bar ourselves programmatically, when using our hidden

 214      iframe, we need to ignore these changes. Unfortunately,

 215      these changes are not atomic, so we surround them with

 216      the variable 'ieAtomicLocationChange', that if true,

 217      means we are programmatically setting the location and

 218      should ignore this atomic chunked change. */
 219  /** private */ ieAtomicLocationChange: null, 
 220  
 221  /** Creates the DHTML history infrastructure. */

 222  /** private */ create: function() {
 223      // get our initial location

 224      var initialHash = this.getCurrentLocation();
 225      
 226      // save this as our current location

 227      this.currentLocation = initialHash;
 228      
 229      // write out a hidden iframe for IE and

 230      // set the amount of time to wait between add() requests

 231      if (this.isInternetExplorer()) {
 232          document.write("<iframe style='border: 0px; width: 1px; "
 233                              + "height: 1px; position: absolute; bottom: 0px; "
 234                              + "right: 0px; visibility: visible;' "
 235                              + "name='DhtmlHistoryFrame' id='DhtmlHistoryFrame' "
 236                              + "src='blank.html?" + initialHash + "'>"
 237                              + "</iframe>");
 238          // wait 400 milliseconds between history

 239          // updates on IE, versus 200 on Firefox

 240          this.WAIT_TIME = 400;
 241      }
 242      
 243      // add an unload listener for the page; this is

 244      // needed for Firefox 1.5+ because this browser caches all

 245      // dynamic updates to the page, which can break some of our 

 246      // logic related to testing whether this is the first instance

 247      // a page has loaded or whether it is being pulled from the cache

 248      var self = this;
 249      window.onunload = function() {
 250          self.firstLoad = null;
 251      };
 252      
 253      // determine if this is our first page load;

 254      // for Internet Explorer, we do this in 

 255      // this.iframeLoaded(), which is fired on

 256      // page load. We do it there because

 257      // we have no historyStorage at this point

 258      // in IE, which only exists after the page

 259      // is finished loading for that browser

 260      if (this.isInternetExplorer() == false) {
 261          if (historyStorage.hasKey("DhtmlHistory_pageLoaded") == false) {
 262              this.ignoreLocationChange = true;
 263              this.firstLoad = true;
 264              historyStorage.put("DhtmlHistory_pageLoaded", true);
 265          }
 266          else {
 267              // indicate that we want to pay attention

 268              // to this location change

 269              this.ignoreLocationChange = false;
 270              // For browser's other than IE, fire

 271              // a history change event; on IE,

 272              // the event will be thrown automatically

 273              // when it's hidden iframe reloads

 274              // on page load.

 275              // Unfortunately, we don't have any 

 276              // listeners yet; indicate that we want

 277              // to fire an event when a listener

 278              // is added.

 279              this.fireOnNewListener = true;
 280          }
 281      }
 282      else { // Internet Explorer
 283          // the iframe will get loaded on page

 284          // load, and we want to ignore this fact

 285          this.ignoreLocationChange = true;
 286      }
 287      
 288      if (this.isInternetExplorer()) {
 289              this.iframe = document.getElementById("DhtmlHistoryFrame");
 290      } 
 291  
 292      // other browsers can use a location handler that checks

 293      // at regular intervals as their primary mechanism;

 294      // we use it for Internet Explorer as well to handle

 295      // an important edge case; see checkLocation() for

 296      // details

 297      var self = this;
 298      var locationHandler = function() {
 299          self.checkLocation();
 300      };
 301      setInterval(locationHandler, 100);
 302  },
 303  
 304  /** Notify the listener of new history changes. */

 305  /** private */ fireHistoryEvent: function(newHash) {
 306      // extract the value from our history storage for

 307      // this hash

 308      var historyData = historyStorage.get(newHash);
 309  
 310      // call our listener

 311      this.listener.call(null, newHash, historyData);
 312  },
 313  
 314  /** Sees if the browsers has changed location.  This is the primary history mechanism

 315      for Firefox. For Internet Explorer, we use this to handle an important edge case:

 316      if a user manually types in a new hash value into their Internet Explorer location

 317      bar and press enter, we want to intercept this and notify any history listener. */
 318  /** private */ checkLocation: function() {
 319      // ignore any location changes that we made ourselves

 320      // for browsers other than Internet Explorer

 321      if (this.isInternetExplorer() == false
 322          && this.ignoreLocationChange == true) {
 323          this.ignoreLocationChange = false;
 324          return;
 325      }
 326      
 327      // if we are dealing with Internet Explorer

 328      // and we are in the middle of making a location

 329      // change from an iframe, ignore it

 330      if (this.isInternetExplorer() == false
 331          && this.ieAtomicLocationChange == true) {
 332          return;
 333      }
 334      
 335      // get hash location

 336      var hash = this.getCurrentLocation();
 337      
 338      // see if there has been a change

 339      if (hash == this.currentLocation)
 340          return;
 341          
 342      // on Internet Explorer, we need to intercept users manually

 343      // entering locations into the browser; we do this by comparing

 344      // the browsers location against the iframes location; if they

 345      // differ, we are dealing with a manual event and need to

 346      // place it inside our history, otherwise we can return

 347      this.ieAtomicLocationChange = true;
 348      
 349      if (this.isInternetExplorer()
 350          && this.getIFrameHash() != hash) {
 351          this.iframe.src = "blank.html?" + hash;
 352      }
 353      else if (this.isInternetExplorer()) {
 354          // the iframe is unchanged

 355          return;
 356      }
 357          
 358      // save this new location

 359      this.currentLocation = hash;
 360      
 361      this.ieAtomicLocationChange = false;
 362      
 363      // notify listeners of the change

 364      this.fireHistoryEvent(hash);
 365  },  
 366  
 367  /** Gets the current location of the hidden IFrames

 368      that is stored as history. For Internet Explorer. */
 369  /** private */ getIFrameHash: function() {
 370      // get the new location

 371      var historyFrame = document.getElementById("DhtmlHistoryFrame");
 372      var doc = historyFrame.contentWindow.document;
 373      var hash = new String(doc.location.search);
 374  
 375      if (hash.length == 1 && hash.charAt(0) == "?")
 376          hash = "";
 377      else if (hash.length >= 2 && hash.charAt(0) == "?")
 378          hash = hash.substring(1); 
 379      
 380      
 381      return hash;
 382  },        
 383  
 384  /** Removes any leading hash that might be on a location. */

 385  /** private */ removeHash: function(hashValue) {
 386      if (hashValue == null || hashValue == undefined)
 387          return null;
 388      else if (hashValue == "")
 389          return "";
 390      else if (hashValue.length == 1 && hashValue.charAt(0) == "#")
 391          return "";
 392      else if (hashValue.length > 1 && hashValue.charAt(0) == "#")
 393          return hashValue.substring(1);
 394      else
 395          return hashValue;    
 396  },        
 397  
 398  /** For IE, says when the hidden iframe has finished loading. */

 399  /** private */ iframeLoaded: function(newLocation) {
 400      // ignore any location changes that we made ourselves

 401      if (this.ignoreLocationChange == true) {
 402          this.ignoreLocationChange = false;
 403          return;
 404      }
 405      
 406      // get the new location

 407      var hash = new String(newLocation.search);
 408      if (hash.length == 1 && hash.charAt(0) == "?")
 409          hash = "";
 410      else if (hash.length >= 2 && hash.charAt(0) == "?")
 411          hash = hash.substring(1);
 412      
 413      // move to this location in the browser location bar

 414      // if we are not dealing with a page load event

 415      if (this.pageLoadEvent != true) {
 416          window.location.hash = hash;
 417      }
 418  
 419      // notify listeners of the change

 420      this.fireHistoryEvent(hash);
 421  },
 422  
 423  /** Determines if this is Internet Explorer. */

 424  /** private */ isInternetExplorer: function() {
 425      var userAgent = navigator.userAgent.toLowerCase();
 426      if (document.all && userAgent.indexOf('msie')!=-1) {
 427          return true;
 428      }
 429      else {
 430          return false;
 431      }
 432  }
 433  };
 434  
 435  
 436  
 437  
 438  
 439  
 440  
 441  
 442  
 443  
 444  
 445  
 446  /** An object that uses a hidden form to store history state 

 447      across page loads. The chief mechanism for doing so is using

 448      the fact that browser's save the text in form data for the

 449      life of the browser and cache, which means the text is still

 450      there when the user navigates back to the page. See

 451      http://codinginparadise.org/weblog/2005/08/ajax-tutorial-saving-session-across.html

 452      for full details. */
 453  window.historyStorage = {
 454  /** If true, we are debugging and show the storage textfield. */

 455  /** public */ debugging: false,
 456  
 457  /** Our hash of key name/values. */

 458  /** private */ storageHash: new Object(),
 459  
 460  /** If true, we have loaded our hash table out of the storage form. */

 461  /** private */ hashLoaded: false, 
 462  
 463  /** public */ put: function(key, value) {
 464      this.assertValidKey(key);
 465      
 466      // if we already have a value for this,

 467      // remove the value before adding the

 468      // new one

 469      if (this.hasKey(key)) {
 470          this.remove(key);
 471      }
 472      
 473      // store this new key

 474      this.storageHash[key] = value;
 475      
 476      // save and serialize the hashtable into the form

 477      this.saveHashTable(); 
 478  },
 479  
 480  /** public */ get: function(key) {
 481      this.assertValidKey(key);
 482      
 483      // make sure the hash table has been loaded

 484      // from the form

 485      this.loadHashTable();
 486      
 487      var value = this.storageHash[key];
 488  
 489      if (value == undefined)
 490          return null;
 491      else
 492          return value; 
 493  },
 494  
 495  /** public */ remove: function(key) {
 496      this.assertValidKey(key);
 497      
 498      // make sure the hash table has been loaded

 499      // from the form

 500      this.loadHashTable();
 501      
 502      // delete the value

 503      delete this.storageHash[key];
 504      
 505      // serialize and save the hash table into the 

 506      // form

 507      this.saveHashTable();
 508  },
 509  
 510  /** Clears out all saved data. */

 511  /** public */ reset: function() {
 512      this.storageField.value = "";
 513      this.storageHash = new Object();
 514  },
 515  
 516  /** public */ hasKey: function(key) {
 517      this.assertValidKey(key);
 518      
 519      // make sure the hash table has been loaded

 520      // from the form

 521      this.loadHashTable();
 522      
 523      if (typeof this.storageHash[key] == "undefined")
 524          return false;
 525      else
 526          return true;
 527  },
 528  
 529  /** Determines whether the key given is valid;

 530      keys can only have letters, numbers, the dash,

 531      underscore, spaces, or one of the 

 532      following characters:

 533      !@#$%^&*()+=:;,./?|\~{}[] */
 534  /** public */ isValidKey: function(key) {
 535      // allow all strings, since we don't use XML serialization

 536      // format anymore

 537      return (typeof key == "string");
 538      
 539      /*

 540      if (typeof key != "string")

 541          key = key.toString();

 542      

 543      

 544      var matcher = 

 545          /^[a-zA-Z0-9_ \!\@\#\$\%\^\&\*\(\)\+\=\:\;\,\.\/\?\|\\\~\{\}\[\]]*$/;

 546                      

 547      return matcher.test(key);*/
 548  },
 549  
 550  
 551  
 552  
 553  /** A reference to our textarea field. */

 554  /** private */ storageField: null,
 555  
 556  /** private */ init: function() {
 557      // write a hidden form into the page

 558      var styleValue = "position: absolute; top: -1000px; left: -1000px;";
 559      if (this.debugging == true) {
 560          styleValue = "width: 30em; height: 30em;";
 561      }
 562      
 563      var newContent =
 564          "<form id='historyStorageForm' " 
 565              + "method='GET' "
 566              + "style='" + styleValue + "'>"
 567              + "<textarea id='historyStorageField' "
 568                      + "style='" + styleValue + "'"
 569                              + "left: -1000px;' "
 570                      + "name='historyStorageField'></textarea>"
 571          + "</form>";
 572      document.write(newContent);
 573      
 574      this.storageField = document.getElementById("historyStorageField");
 575  },
 576  
 577  /** Asserts that a key is valid, throwing

 578      an exception if it is not. */
 579  /** private */ assertValidKey: function(key) {
 580      if (this.isValidKey(key) == false) {
 581          throw "Please provide a valid key for "
 582              + "window.historyStorage, key= "
 583              + key;
 584      }
 585  },
 586  
 587  /** Loads the hash table up from the form. */

 588  /** private */ loadHashTable: function() {
 589      if (this.hashLoaded == false) {
 590          // get the hash table as a serialized

 591          // string

 592          var serializedHashTable = this.storageField.value;
 593          
 594          if (serializedHashTable != "" &&
 595              serializedHashTable != null) {
 596              // destringify the content back into a 

 597              // real JavaScript object

 598              this.storageHash = eval('(' + serializedHashTable + ')');  
 599          }
 600          
 601          this.hashLoaded = true;
 602      }
 603  },
 604  
 605  /** Saves the hash table into the form. */

 606  /** private */ saveHashTable: function() {
 607      this.loadHashTable();
 608      
 609      // serialized the hash table

 610      var serializedHashTable = HTML_AJAX_JSON.stringify(this.storageHash);
 611      
 612      // save this value

 613      this.storageField.value = serializedHashTable;
 614  }
 615  };
 616  
 617  /** Initialize all of our objects now. */

 618  window.historyStorage.init();
 619  window.dhtmlHistory.create();


Généré le : Fri Mar 30 01:27:52 2007 par Balluche grâce à PHPXref 0.7