/adei/ui

To get this branch, use:
bzr branch http://darksoft.org/webbzr/adei/ui

« back to all changes in this revision

Viewing changes to includes/rsh.js

  • Committer: Suren A. Chilingaryan
  • Date: 2008-04-02 10:23:22 UTC
  • Revision ID: csa@dside.dyndns.org-20080402102322-okib92sicg2dx3o3
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2007 Brian Dillard and Brad Neuberg:
 
3
Brian Dillard | Project Lead | bdillard@pathf.com | http://blogs.pathf.com/agileajax/
 
4
Brad Neuberg | Original Project Creator | http://codinginparadise.org
 
5
   
 
6
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
 
7
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
 
8
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
 
9
so, subject to the following conditions:
 
10
 
 
11
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
12
 
 
13
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
14
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 
15
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
16
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
17
*/
 
18
 
 
19
/*
 
20
        dhtmlHistory: An object that provides history, history data, and bookmarking for DHTML and Ajax applications.
 
21
        
 
22
        dependencies:
 
23
                * the historyStorage object included in this file.
 
24
 
 
25
*/
 
26
window.dhtmlHistory = {
 
27
        
 
28
        /*Public: User-agent booleans*/
 
29
        isIE: false,
 
30
        isOpera: false,
 
31
        isSafari: false,
 
32
        isKonquerer: false,
 
33
        isGecko: false,
 
34
        isSupported: false,
 
35
        
 
36
        /*Public: Create the DHTML history infrastructure*/
 
37
        create: function(options) {
 
38
                
 
39
                /*
 
40
                        options - object to store initialization parameters
 
41
                        options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
 
42
                        options.toJSON - function to override default JSON stringifier
 
43
                        options.fromJSON - function to override default JSON parser
 
44
                */
 
45
 
 
46
                var that = this;
 
47
 
 
48
                /*set user-agent flags*/
 
49
                var UA = navigator.userAgent.toLowerCase();
 
50
                var platform = navigator.platform.toLowerCase();
 
51
                var vendor = navigator.vendor || "";
 
52
                if (vendor === "KDE") {
 
53
                        this.isKonqueror = true;
 
54
                        this.isSupported = false;
 
55
                } else if (typeof window.opera !== "undefined") {
 
56
                        this.isOpera = true;
 
57
                        this.isSupported = true;
 
58
                } else if (typeof document.all !== "undefined") {
 
59
                        this.isIE = true;
 
60
                        this.isSupported = true;
 
61
                } else if (vendor.indexOf("Apple Computer, Inc.") > -1) {
 
62
                        this.isSafari = true;
 
63
                        this.isSupported = true; //(platform.indexOf("mac") > -1);
 
64
                } else if (UA.indexOf("gecko") != -1) {
 
65
                        this.isGecko = true;
 
66
                        this.isSupported = true;
 
67
                }
 
68
 
 
69
                /*Set up the historyStorage object; pass in init parameters*/
 
70
                window.historyStorage.setup(options);
 
71
 
 
72
                /*Execute browser-specific setup methods*/
 
73
                if (this.isSafari) {
 
74
                        this.createSafari();
 
75
                } else if (this.isOpera) {
 
76
                        this.createOpera();
 
77
                }
 
78
                
 
79
                /*Get our initial location*/
 
80
                var initialHash = this.getCurrentLocation();
 
81
 
 
82
                /*Save it as our current location*/
 
83
                this.currentLocation = initialHash;
 
84
 
 
85
                /*Now that we have a hash, create IE-specific code*/
 
86
                if (this.isIE) {
 
87
                        this.createIE(initialHash);
 
88
                }
 
89
 
 
90
                /*Add an unload listener for the page; this is needed for FF 1.5+ because this browser caches all dynamic updates to the
 
91
                page, which can break some of our logic related to testing whether this is the first instance a page has loaded or whether
 
92
                it is being pulled from the cache*/
 
93
 
 
94
                var unloadHandler = function() {
 
95
                        that.firstLoad = null;
 
96
                };
 
97
                
 
98
                this.addEventListener(window,'unload',unloadHandler);           
 
99
 
 
100
                /*Determine if this is our first page load; for IE, we do this in this.iframeLoaded(), which is fired on pageload. We do it
 
101
                there because we have no historyStorage at this point, which only exists after the page is finished loading in IE*/
 
102
                if (this.isIE) {
 
103
                        /*The iframe will get loaded on page load, and we want to ignore this fact*/
 
104
                        this.ignoreLocationChange = true;
 
105
                } else {
 
106
                        if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
 
107
                                /*This is our first page load, so ignore the location change and add our special history entry*/
 
108
                                this.ignoreLocationChange = true;
 
109
                                this.firstLoad = true;
 
110
                                historyStorage.put(this.PAGELOADEDSTRING, true);
 
111
                        } else {
 
112
                                /*This isn't our first page load, so indicate that we want to pay attention to this location change*/
 
113
                                this.ignoreLocationChange = false;
 
114
                                /*For browsers other than IE, fire a history change event; on IE, the event will be thrown automatically when its
 
115
                                hidden iframe reloads on page load. Unfortunately, we don't have any listeners yet; indicate that we want to fire
 
116
                                an event when a listener is added.*/
 
117
                                this.fireOnNewListener = true;
 
118
                        }
 
119
                }
 
120
 
 
121
                /*Other browsers can use a location handler that checks at regular intervals as their primary mechanism; we use it for IE as
 
122
                well to handle an important edge case; see checkLocation() for details*/
 
123
                var locationHandler = function() {
 
124
                        that.checkLocation();
 
125
                };
 
126
                setInterval(locationHandler, 100);
 
127
        },      
 
128
        
 
129
        /*Public: Initialize our DHTML history. You must call this after the page is finished loading.*/
 
130
        initialize: function() {
 
131
                /*IE needs to be explicitly initialized. IE doesn't autofill form data until the page is finished loading, so we have to wait*/
 
132
                if (this.isIE) {
 
133
                        /*If this is the first time this page has loaded*/
 
134
                        if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
 
135
                                /*For IE, we do this in initialize(); for other browsers, we do it in create()*/
 
136
                                this.fireOnNewListener = false;
 
137
                                this.firstLoad = true;
 
138
                                historyStorage.put(this.PAGELOADEDSTRING, true);
 
139
                        }
 
140
                        /*Else if this is a fake onload event*/
 
141
                        else {
 
142
                                this.fireOnNewListener = true;
 
143
                                this.firstLoad = false;   
 
144
                        }
 
145
                }
 
146
        },
 
147
 
 
148
        /*Public: Adds a history change listener. Note that only one listener is supported at this time.*/
 
149
        addListener: function(listener) {
 
150
                this.listener = listener;
 
151
                /*If the page was just loaded and we should not ignore it, fire an event to our new listener now*/
 
152
                if (this.fireOnNewListener) {
 
153
                        this.fireHistoryEvent(this.currentLocation);
 
154
                        this.fireOnNewListener = false;
 
155
                }
 
156
        },
 
157
        
 
158
        /*Public: Generic utility function for attaching events*/
 
159
        addEventListener: function(o,e,l) {
 
160
                if (o.addEventListener) {
 
161
                        o.addEventListener(e,l,false);
 
162
                } else if (o.attachEvent) {
 
163
                        o.attachEvent('on'+e,function() {
 
164
                                l(window.event);
 
165
                        });
 
166
                }
 
167
        },
 
168
        
 
169
        /*Public: Add a history point.*/
 
170
        add: function(newLocation, historyData) {
 
171
 
 
172
                if (this.isSafari) {
 
173
                        
 
174
                        /*Remove any leading hash symbols on newLocation*/
 
175
                        newLocation = this.removeHash(newLocation);
 
176
 
 
177
                        /*Store the history data into history storage*/
 
178
                        historyStorage.put(newLocation, historyData);
 
179
 
 
180
                        /*Save this as our current location*/
 
181
                        this.currentLocation = newLocation;
 
182
        
 
183
                        /*Change the browser location*/
 
184
                        window.location.hash = newLocation;
 
185
                
 
186
                        /*Save this to the Safari form field*/
 
187
                        this.putSafariState(newLocation);
 
188
 
 
189
                } else {
 
190
                        
 
191
                        /*Most browsers require that we wait a certain amount of time before changing the location, such
 
192
                        as 200 MS; rather than forcing external callers to use window.setTimeout to account for this,
 
193
                        we internally handle it by putting requests in a queue.*/
 
194
                        var that = this;
 
195
                        var addImpl = function() {
 
196
                                var fixWaitTime = function() {
 
197
                                    /*Indicate that the current wait time is now less*/
 
198
                                    if (that.currentWaitTime > 0) {
 
199
                                        that.currentWaitTime = that.currentWaitTime - that.waitTime;
 
200
                                    }
 
201
                                    
 
202
                                    /*End of atomic location change block for IE*/
 
203
                                    that.ieAtomicLocationChange = false;
 
204
                                }
 
205
 
 
206
                                /*Remove any leading hash symbols on newLocation*/
 
207
                                newLocation = that.removeHash(newLocation);
 
208
 
 
209
                                /*IE has a strange bug; if the newLocation is the same as _any_ preexisting id in the
 
210
                                document, then the history action gets recorded twice; throw a programmer exception if
 
211
                                there is an element with this ID*/
 
212
                                if (document.getElementById(newLocation) && that.debugMode) {
 
213
                                        var e = "Exception: History locations can not have the same value as _any_ IDs that might be in the document,"
 
214
                                        + " due to a bug in IE; please ask the developer to choose a history location that does not match any HTML"
 
215
                                        + " IDs in this document. The following ID is already taken and cannot be a location: " + newLocation;
 
216
                                        throw new Error(e); 
 
217
                                }
 
218
 
 
219
                                /*Store the history data into history storage*/
 
220
                                historyStorage.put(newLocation, historyData);
 
221
 
 
222
                                /*Indicate to the browser to ignore this upcomming location change since we're making it programmatically*/
 
223
                                that.ignoreLocationChange = true;
 
224
 
 
225
                                /*Indicate to IE that this is an atomic location change block*/
 
226
                                that.ieAtomicLocationChange = true;
 
227
 
 
228
                                /*Save this as our current location*/
 
229
                                that.currentLocation = newLocation;
 
230
                
 
231
                                /*Change the browser location*/
 
232
                                window.location.hash = newLocation;
 
233
 
 
234
                                /*Change the hidden iframe's location if on IE*/
 
235
                                if (that.isIE) {
 
236
                                        that.iframe.src = "blank.html?" + newLocation;
 
237
                                }
 
238
                                
 
239
                                if (that.waitTime) {
 
240
                                    window.setTimeout(fixWaitTime, that.waitTime);
 
241
                                } else {
 
242
                                    /*End of atomic location change block for IE*/
 
243
                                    that.ieAtomicLocationChange = false;
 
244
                                }
 
245
                        };
 
246
                            
 
247
                            // Check?
 
248
                        this.ieAtomicLocationChange = true;
 
249
                        
 
250
                        if (this.currentWaitTime) {
 
251
                            /*Now queue up this add request*/
 
252
                            window.setTimeout(addImpl, this.currentWaitTime);
 
253
 
 
254
                            /*Indicate that the next request will have to wait for awhile*/
 
255
                            this.currentWaitTime = this.currentWaitTime + this.waitTime;
 
256
                        } else {
 
257
                            this.currentWaitTime = this.currentWaitTime + this.waitTime;
 
258
                            addImpl();
 
259
                        }
 
260
 
 
261
                }
 
262
        },
 
263
 
 
264
        /*Public*/
 
265
        isFirstLoad: function() {
 
266
                return this.firstLoad;
 
267
        },
 
268
 
 
269
        /*Public*/
 
270
        getVersion: function() {
 
271
                return "0.6";
 
272
        },
 
273
 
 
274
        /*Get browser's current hash location; for Safari, read value from a hidden form field*/
 
275
 
 
276
        /*Public*/
 
277
        getCurrentLocation: function() {
 
278
                return this.getCurrentHash();
 
279
        /*
 
280
            This causes problems in Safari and looks not really needed,
 
281
            at least Mac 3.0.4 and Win 3.1 working well without special
 
282
            handling of Safari.
 
283
                var r = (this.isSafari
 
284
                        ? this.getSafariState()
 
285
                        : this.getCurrentHash()
 
286
                );
 
287
                return r;
 
288
        */
 
289
        },
 
290
        
 
291
        /*Public: Manually parse the current url for a hash; tip of the hat to YUI*/
 
292
    getCurrentHash: function() {
 
293
            if (this.isIE) {
 
294
                var r = this.getIframeHash();
 
295
                if (r) return r;
 
296
            }
 
297
                
 
298
                var r = window.location.href;
 
299
                var i = r.indexOf("#");
 
300
                return (i >= 0
 
301
                        ? r.substr(i+1)
 
302
                        : ""
 
303
                );
 
304
    },
 
305
        
 
306
        /*- - - - - - - - - - - -*/
 
307
        
 
308
        /*Private: Constant for our own internal history event called when the page is loaded*/
 
309
        PAGELOADEDSTRING: "DhtmlHistory_pageLoaded",
 
310
        
 
311
        /*Private: Our history change listener.*/
 
312
        listener: null,
 
313
 
 
314
        /*Private: MS to wait between add requests - will be reset for certain browsers*/
 
315
        waitTime: 200,
 
316
        
 
317
        /*Private: MS before an add request can execute*/
 
318
        currentWaitTime: 0,
 
319
 
 
320
        /*Private: Our current hash location, without the "#" symbol.*/
 
321
        currentLocation: null,
 
322
 
 
323
        /*Private: Hidden iframe used to IE to detect history changes*/
 
324
        iframe: null,
 
325
 
 
326
        /*Private: Flags and DOM references used only by Safari*/
 
327
        safariHistoryStartPoint: null,
 
328
        safariStack: null,
 
329
        safariLength: null,
 
330
 
 
331
        /*Private: Flag used to keep checkLocation() from doing anything when it discovers location changes we've made ourselves
 
332
        programmatically with the add() method. Basically, add() sets this to true. When checkLocation() discovers it's true,
 
333
        it refrains from firing our listener, then resets the flag to false for next cycle. That way, our listener only gets fired on
 
334
        history change events triggered by the user via back/forward buttons and manual hash changes. This flag also helps us set up
 
335
        IE's special iframe-based method of handling history changes.*/
 
336
        ignoreLocationChange: null,
 
337
 
 
338
        /*Private: A flag that indicates that we should fire a history change event when we are ready, i.e. after we are initialized and
 
339
        we have a history change listener. This is needed due to an edge case in browsers other than IE; if you leave a page entirely
 
340
        then return, we must fire this as a history change event. Unfortunately, we have lost all references to listeners from earlier,
 
341
        because JavaScript clears out.*/
 
342
        fireOnNewListener: null,
 
343
 
 
344
        /*Private: A variable that indicates whether this is the first time this page has been loaded. If you go to a web page, leave it
 
345
        for another one, and then return, the page's onload listener fires again. We need a way to differentiate between the first page
 
346
        load and subsequent ones. This variable works hand in hand with the pageLoaded variable we store into historyStorage.*/
 
347
        firstLoad: null,
 
348
 
 
349
        /*Private: A variable to handle an important edge case in IE. In IE, if a user manually types an address into their browser's
 
350
        location bar, we must intercept this by calling checkLocation() at regular intervals. However, if we are programmatically
 
351
        changing the location bar ourselves using the add() method, we need to ignore these changes in checkLocation(). Unfortunately,
 
352
        these changes take several lines of code to complete, so for the duration of those lines of code, we set this variable to true.
 
353
        That signals to checkLocation() to ignore the change-in-progress. Once we're done with our chunk of location-change code in
 
354
        add(), we set this back to false. We'll do the same thing when capturing user-entered address changes in checkLocation itself.*/
 
355
        ieAtomicLocationChange: null,
 
356
        
 
357
        /*Private: Create IE-specific DOM nodes and overrides*/
 
358
        createIE: function(initialHash) {
 
359
                /*write out a hidden iframe for IE and set the amount of time to wait between add() requests*/
 
360
                this.waitTime = 400;/*IE needs longer between history updates*/
 
361
                var styles = (historyStorage.debugMode
 
362
                        ? 'width: 800px;height:80px;border:1px solid black;'
 
363
                        : historyStorage.hideStyles
 
364
                );
 
365
                var iframeID = "rshHistoryFrame";
 
366
/*
 
367
                var iframeHTML = '<iframe frameborder="0" id="' + iframeID + '" style="' + styles + '" src="blank.html?' + initialHash + '"></iframe>';
 
368
                document.write(iframeHTML);
 
369
                this.iframe = document.getElementById(iframeID);
 
370
*/
 
371
 
 
372
 
 
373
                var node = document.createElement('iframe');
 
374
                node.setAttribute('frameborder', '0');
 
375
                node.setAttribute('id', iframeID);
 
376
                if (typeof node.style.cssText == "string")
 
377
                    node.style.cssText = styles;
 
378
                else
 
379
                    node.setAttribute('style', styles);
 
380
                
 
381
                node.setAttribute("src", "blank.html?" + initialHash);
 
382
                this.iframe = node;
 
383
 
 
384
                document.body.appendChild(node);
 
385
        },
 
386
        
 
387
        /*Private: Create Opera-specific DOM nodes and overrides*/
 
388
        createOpera: function() {
 
389
                this.waitTime = 400;/*Opera needs longer between history updates*/
 
390
/*
 
391
                var imgHTML = '<img src="javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';" style="' + historyStorage.hideStyles + '" />';
 
392
                document.write(imgHTML);
 
393
*/
 
394
                var node = document.createElement('img');
 
395
                if (typeof node.style.cssText == "string")
 
396
                    node.style.cssText = historyStorage.hideStyles;
 
397
                else
 
398
                    node.setAttribute('style', historyStorage.hideStyles);
 
399
                node.setAttribute('src', "javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';");
 
400
 
 
401
                document.body.appendChild(node);
 
402
 
 
403
        },
 
404
        
 
405
        /*Private: Create Safari-specific DOM nodes and overrides*/
 
406
        createSafari: function() {
 
407
                var formID = "rshSafariForm";
 
408
                var stackID = "rshSafariStack";
 
409
                var lengthID = "rshSafariLength";
 
410
                var formStyles = historyStorage.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
 
411
                var inputStyles = (historyStorage.debugMode
 
412
                        ? 'width:800px;height:20px;border:1px solid black;margin:0;padding:0;'
 
413
                        : historyStorage.hideStyles
 
414
                );
 
415
 
 
416
/*
 
417
                var safariHTML = '<form id="' + formID + '" style="' + formStyles + '">'
 
418
                        + '<input type="text" style="' + inputStyles + '" id="' + stackID + '" value="[]"/>'
 
419
                        + '<input type="text" style="' + inputStyles + '" id="' + lengthID + '" value=""/>'
 
420
                + '</form>';
 
421
                document.write(safariHTML);
 
422
                this.safariStack = document.getElementById(stackID);
 
423
                this.safariLength = document.getElementById(lengthID);
 
424
*/
 
425
 
 
426
                var fnode = document.createElement('form');
 
427
                if (typeof fnode.style.cssText == "string")
 
428
                    fnode.style.cssText = formStyles;
 
429
                else
 
430
                    fnode.setAttribute('style', formStyles);
 
431
                fnode.setAttribute('id', formID);
 
432
                
 
433
                var node = document.createElement('input');
 
434
                node.setAttribute('type', "text");
 
435
                if (typeof node.style.cssText == "string")
 
436
                    node.style.cssText = inputStyles;
 
437
                else
 
438
                    node.setAttribute('style', inputStyles);
 
439
                node.setAttribute('id', stackID);
 
440
                node.setAttribute('value', "[]");
 
441
 
 
442
                fnode.appendChild(node);
 
443
                this.safariStack = node;
 
444
                
 
445
                node = document.createElement('input');
 
446
                node.setAttribute('type', "text");
 
447
                if (typeof node.style.cssText == "string")
 
448
                    node.style.cssText = inputStyles;
 
449
                else
 
450
                    node.setAttribute('style', inputStyles);
 
451
                node.setAttribute('id', lengthID);
 
452
                node.setAttribute('value', "");
 
453
 
 
454
                fnode.appendChild(node);
 
455
                this.safariLength = node;
 
456
                
 
457
                document.body.appendChild(fnode);
 
458
 
 
459
                if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
 
460
                        this.safariHistoryStartPoint = history.length;
 
461
                        this.safariLength.value = this.safariHistoryStartPoint;
 
462
                } else {
 
463
                        this.safariHistoryStartPoint = this.safariLength.value;
 
464
                }
 
465
        },
 
466
        
 
467
        /*Private: Safari method to read the history stack from a hidden form field*/
 
468
        getSafariStack: function() {
 
469
                var r = this.safariStack.value;
 
470
                return historyStorage.fromJSON(r);
 
471
        },
 
472
 
 
473
        /*Private: Safari method to read from the history stack*/
 
474
        getSafariState: function() {
 
475
                var stack = this.getSafariStack();
 
476
                var state = stack[history.length - this.safariHistoryStartPoint - 1];
 
477
                return state;
 
478
        },                      
 
479
        /*Private: Safari method to write the history stack to a hidden form field*/
 
480
        putSafariState: function(newLocation) {
 
481
            var stack = this.getSafariStack();
 
482
            stack[history.length - this.safariHistoryStartPoint] = newLocation;
 
483
            this.safariStack.value = historyStorage.toJSON(stack);
 
484
        },
 
485
 
 
486
        /*Private: Notify the listener of new history changes.*/
 
487
        fireHistoryEvent: function(newHash) {
 
488
                /*extract the value from our history storage for this hash*/
 
489
                var historyData = historyStorage.get(newHash);
 
490
                /*call our listener*/
 
491
                this.listener.call(null, newHash, historyData);
 
492
        },
 
493
        
 
494
        /*Private: See if the browser has changed location. This is the primary history mechanism for Firefox. For IE, we use this to
 
495
        handle an important edge case: if a user manually types in a new hash value into their IE location bar and press enter, we want to
 
496
        to intercept this and notify any history listener.*/
 
497
        checkLocation: function() {
 
498
                
 
499
                /*Ignore any location changes that we made ourselves for browsers other than IE*/
 
500
                if (!this.isIE && this.ignoreLocationChange) {
 
501
                        this.ignoreLocationChange = false;
 
502
                        return;
 
503
                }
 
504
 
 
505
                /*If we are dealing with IE and we are in the middle of making a location change from an iframe, ignore it*/
 
506
                if (this.isIE && this.ieAtomicLocationChange) {
 
507
                        return;
 
508
                }
 
509
                
 
510
                /*Get hash location*/
 
511
                var hash = this.getCurrentLocation();
 
512
 
 
513
                /*Do nothing if there's been no change*/
 
514
                if (hash == this.currentLocation) {
 
515
                        return;
 
516
                }
 
517
//              alert(hash + ' - ' + this.currentLocation);
 
518
                
 
519
                /*In IE, users manually entering locations into the browser; we do this by comparing the browser's location against the
 
520
                iframe's location; if they differ, we are dealing with a manual event and need to place it inside our history, otherwise
 
521
                we can return*/
 
522
                this.ieAtomicLocationChange = true;
 
523
 
 
524
                if (this.isIE) {
 
525
                    this.iframe.src = "blank.html?" + hash;
 
526
                    window.location.hash = hash;
 
527
                }
 
528
 
 
529
/*
 
530
                if (this.isIE && this.getIframeHash() != hash) {
 
531
                        this.iframe.src = "blank.html?" + hash;
 
532
                }
 
533
                else if (this.isIE) {
 
534
                        return;
 
535
                }
 
536
*/
 
537
 
 
538
                /*Save this new location*/
 
539
                this.currentLocation = hash;
 
540
 
 
541
                this.ieAtomicLocationChange = false;
 
542
 
 
543
                /*Notify listeners of the change*/
 
544
                this.fireHistoryEvent(hash);
 
545
        },
 
546
 
 
547
        /*Private: Get the current location of IE's hidden iframe.*/
 
548
        getIframeHash: function() {
 
549
                if (!this.iframe) return "";
 
550
                
 
551
                var doc = this.iframe.contentWindow.document;
 
552
                var hash = String(doc.location.search);
 
553
                if (hash.length == 1 && hash.charAt(0) == "?") {
 
554
                        hash = "";
 
555
                }
 
556
                else if (hash.length >= 2 && hash.charAt(0) == "?") {
 
557
                        hash = hash.substring(1);
 
558
                }
 
559
                return hash;
 
560
        },
 
561
 
 
562
        /*Private: Remove any leading hash that might be on a location.*/
 
563
        removeHash: function(hashValue) {
 
564
                var r;
 
565
                if (hashValue === null || hashValue === undefined) {
 
566
                        r = null;
 
567
                }
 
568
                else if (hashValue === "") {
 
569
                        r = "";
 
570
                }
 
571
                else if (hashValue.length == 1 && hashValue.charAt(0) == "#") {
 
572
                        r = "";
 
573
                }
 
574
                else if (hashValue.length > 1 && hashValue.charAt(0) == "#") {
 
575
                        r = hashValue.substring(1);
 
576
                }
 
577
                else {
 
578
                        r = hashValue;
 
579
                }
 
580
                return r;
 
581
        },
 
582
 
 
583
        /*Private: For IE, tell when the hidden iframe has finished loading.*/
 
584
        iframeLoaded: function(newLocation) {
 
585
                /*ignore any location changes that we made ourselves*/
 
586
                if (this.ignoreLocationChange) {
 
587
                        this.ignoreLocationChange = false;
 
588
                        return;
 
589
                }
 
590
 
 
591
                /*Get the new location*/
 
592
                var hash = String(newLocation.search);
 
593
                if (hash.length == 1 && hash.charAt(0) == "?") {
 
594
                        hash = "";
 
595
                }
 
596
                else if (hash.length >= 2 && hash.charAt(0) == "?") {
 
597
                        hash = hash.substring(1);
 
598
                }
 
599
                /*Keep the browser location bar in sync with the iframe hash*/
 
600
                window.location.hash = hash;
 
601
 
 
602
                /*Notify listeners of the change*/
 
603
                this.fireHistoryEvent(hash);
 
604
        }
 
605
 
 
606
};
 
607
 
 
608
/*
 
609
        historyStorage: An object that uses a hidden form to store history state across page loads. The mechanism for doing so relies on
 
610
        the fact that browsers save the text in form data for the life of the browser session, which means the text is still there when
 
611
        the user navigates back to the page. This object can be used independently of the dhtmlHistory object for caching of Ajax
 
612
        session information.
 
613
        
 
614
        dependencies: 
 
615
                * json2007.js (included in a separate file) or alternate JSON methods passed in through an options bundle.
 
616
*/
 
617
window.historyStorage = {
 
618
        
 
619
        /*Public: Set up our historyStorage object for use by dhtmlHistory or other objects*/
 
620
        setup: function(options) {
 
621
                
 
622
                /*
 
623
                        options - object to store initialization parameters - passed in from dhtmlHistory or directly into historyStorage
 
624
                        options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
 
625
                        options.toJSON - function to override default JSON stringifier
 
626
                        options.fromJSON - function to override default JSON parser
 
627
                */
 
628
                
 
629
                /*process init parameters*/
 
630
                if (typeof options !== "undefined") {
 
631
                        if (options.debugMode) {
 
632
                                this.debugMode = options.debugMode;
 
633
                        }
 
634
                        if (options.toJSON) {
 
635
                                this.toJSON = options.toJSON;
 
636
                        }
 
637
                        if (options.fromJSON) {
 
638
                                this.fromJSON = options.fromJSON;
 
639
                        }
 
640
                }               
 
641
                
 
642
                /*write a hidden form and textarea into the page; we'll stow our history stack here*/
 
643
                var formID = "rshStorageForm";
 
644
                var textareaID = "rshStorageField";
 
645
                var formStyles = this.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
 
646
                var textareaStyles = (historyStorage.debugMode
 
647
                        ? 'width: 800px;height:80px;border:1px solid black;'
 
648
                        : historyStorage.hideStyles
 
649
                );
 
650
                
 
651
                /*
 
652
                var textareaHTML = '<form id="' + formID + '" style="' + formStyles + '">'
 
653
                        + '<textarea id="' + textareaID + '" style="' + textareaStyles + '"></textarea>'
 
654
                + '</form>';
 
655
                document.write(textareaHTML);
 
656
                this.storageField = document.getElementById(textareaID);
 
657
                */
 
658
                
 
659
                var node = document.createElement('textarea');
 
660
                node.setAttribute('id', textareaID);
 
661
                if (typeof node.style.cssText == "string")
 
662
                    node.style.cssText = textareaStyles;
 
663
                else
 
664
                    node.setAttribute('style', textareaStyles);
 
665
 
 
666
                var fnode = document.createElement('form');
 
667
                fnode.setAttribute('id', formID);
 
668
                if (typeof fnode.style.cssText == "string")
 
669
                    fnode.style.cssText = formStyles;
 
670
                else
 
671
                    fnode.setAttribute('style', formStyles);
 
672
                fnode.appendChild(node);
 
673
 
 
674
                this.storageField = node;
 
675
                document.body.appendChild(fnode);
 
676
 
 
677
                if (typeof window.opera !== "undefined") {
 
678
                        this.storageField.focus();/*Opera needs to focus this element before persisting values in it*/
 
679
                }
 
680
        },
 
681
        
 
682
        /*Public*/
 
683
        put: function(key, value) {
 
684
                this.assertValidKey(key);
 
685
                /*if we already have a value for this, remove the value before adding the new one*/
 
686
                if (this.hasKey(key)) {
 
687
                        this.remove(key);
 
688
                }
 
689
                /*store this new key*/
 
690
                this.storageHash[key] = value;
 
691
                /*save and serialize the hashtable into the form*/
 
692
                this.saveHashTable();
 
693
        },
 
694
 
 
695
        /*Public*/
 
696
        get: function(key) {
 
697
                this.assertValidKey(key);
 
698
                /*make sure the hash table has been loaded from the form*/
 
699
                this.loadHashTable();
 
700
                var value = this.storageHash[key];
 
701
                if (value === undefined) {
 
702
                        value = null;
 
703
                }
 
704
                return value;
 
705
        },
 
706
 
 
707
        /*Public*/
 
708
        remove: function(key) {
 
709
                this.assertValidKey(key);
 
710
                /*make sure the hash table has been loaded from the form*/
 
711
                this.loadHashTable();
 
712
                /*delete the value*/
 
713
                delete this.storageHash[key];
 
714
                /*serialize and save the hash table into the form*/
 
715
                this.saveHashTable();
 
716
        },
 
717
 
 
718
        /*Public: Clears out all saved data.*/
 
719
        reset: function() {
 
720
                this.storageField.value = "";
 
721
                this.storageHash = {};
 
722
        },
 
723
 
 
724
        /*Public*/
 
725
        hasKey: function(key) {
 
726
                this.assertValidKey(key);
 
727
                /*make sure the hash table has been loaded from the form*/
 
728
                this.loadHashTable();
 
729
                return (typeof this.storageHash[key] !== "undefined");
 
730
        },
 
731
 
 
732
        /*Public*/
 
733
        isValidKey: function(key) {
 
734
                return (typeof key === "string");
 
735
        },
 
736
        
 
737
        /*Public - CSS strings utilized by both objects to hide or show behind-the-scenes DOM elements*/
 
738
        showStyles: 'border:0;margin:0;padding:0;',
 
739
        hideStyles: 'left:-1000px;top:-1000px;width:1px;height:1px;border:0;position:absolute;',
 
740
        
 
741
        /*Public - debug mode flag*/
 
742
        debugMode: false,
 
743
        
 
744
        /*- - - - - - - - - - - -*/
 
745
 
 
746
        /*Private: Our hash of key name/values.*/
 
747
        storageHash: {},
 
748
 
 
749
        /*Private: If true, we have loaded our hash table out of the storage form.*/
 
750
        hashLoaded: false, 
 
751
 
 
752
        /*Private: DOM reference to our history field*/
 
753
        storageField: null,
 
754
 
 
755
        /*Private: Assert that a key is valid; throw an exception if it not.*/
 
756
        assertValidKey: function(key) {
 
757
                var isValid = this.isValidKey(key);
 
758
                if (!isValid && this.debugMode) {
 
759
                        throw new Error("Please provide a valid key for window.historyStorage. Invalid key = " + key + ".");
 
760
                }
 
761
        },
 
762
 
 
763
        /*Private: Load the hash table up from the form.*/
 
764
        loadHashTable: function() {
 
765
                if (!this.hashLoaded) { 
 
766
                        var serializedHashTable = this.storageField.value;
 
767
                        if (serializedHashTable !== "" && serializedHashTable !== null) {
 
768
                                this.storageHash = this.fromJSON(serializedHashTable);
 
769
                                this.hashLoaded = true;
 
770
                        }
 
771
                }
 
772
        },
 
773
        /*Private: Save the hash table into the form.*/
 
774
        saveHashTable: function() {
 
775
                this.loadHashTable();
 
776
                var serializedHashTable = this.toJSON(this.storageHash);
 
777
                this.storageField.value = serializedHashTable;
 
778
        },
 
779
        /*Private: Bridges for our JSON implementations - both rely on 2007 JSON.org library - can be overridden by options bundle*/
 
780
        toJSON: function(o) {
 
781
                return o.toJSONString();
 
782
        },
 
783
        fromJSON: function(s) {
 
784
                return s.parseJSON();
 
785
        }
 
786
};