1 // Options that can be set on the host page:
2 //window.openid_visible_iframe = true; // causes the hidden iframe to show up
3 //window.openid_trace = true; // causes lots of alert boxes
6 if (window.openid_trace) {
7 if (!window.tracediv) {
8 window.tracediv = document.createElement("ol");
9 document.body.appendChild(window.tracediv);
11 var el = document.createElement("li");
12 el.appendChild(document.createTextNode(msg));
13 window.tracediv.appendChild(el);
18 /// <summary>Removes a given element from the array.</summary>
19 /// <returns>True if the element was in the array, or false if it was not found.</returns>
20 Array.prototype.remove = function(element) {
21 function elementToRemoveLast(a, b) {
22 if (a == element) { return 1; }
23 if (b == element) { return -1; }
26 this.sort(elementToRemoveLast);
27 if (this[this.length - 1] == element) {
35 function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url, success_icon_url, failure_icon_url,
36 throttle, timeout, assertionReceivedCode,
37 loginButtonText, loginButtonToolTip, retryButtonText, retryButtonToolTip, busyToolTip,
38 identifierRequiredMessage, loginInProgressMessage,
39 authenticatedByToolTip, authenticatedAsToolTip, authenticationFailedToolTip,
40 discoverCallback, discoveryFailedCallback) {
41 box.dnoi_internal = new Object();
42 if (assertionReceivedCode) {
43 box.dnoi_internal.onauthenticated = function(sender, e) { eval(assertionReceivedCode); }
46 box.dnoi_internal.originalBackground = box.style.background;
47 box.timeout = timeout;
48 box.dnoi_internal.discoverIdentifier = discoverCallback;
49 box.dnoi_internal.authenticationRequests = new Array();
51 // The possible authentication results
52 var authSuccess = new Object();
53 var authRefused = new Object();
54 var timedOut = new Object();
56 function FrameManager(maxFrames) {
57 this.queuedWork = new Array();
58 this.frames = new Array();
59 this.maxFrames = maxFrames;
61 /// <summary>Called to queue up some work that will use an iframe as soon as it is available.</summary>
62 /// <param name="job">
63 /// A delegate that must return the url to point to iframe to.
64 /// Its first parameter is the iframe created to service the request.
65 /// It will only be called when the work actually begins.
67 this.enqueueWork = function(job) {
68 // Assign an iframe to this task immediately if there is one available.
69 if (this.frames.length < this.maxFrames) {
70 this.createIFrame(job);
72 this.queuedWork.unshift(job);
76 /// <summary>Clears the job queue and immediately closes all iframes.</summary>
77 this.cancelAllWork = function() {
78 trace('Canceling all open and pending iframes.');
79 while (this.queuedWork.pop());
83 /// <summary>An event fired when a frame is closing.</summary>
84 this.onJobCompleted = function() {
85 // If there is a job in the queue, go ahead and start it up.
86 if (job = this.queuedWork.pop()) {
87 this.createIFrame(job);
91 this.createIFrame = function(job) {
92 var iframe = document.createElement("iframe");
93 if (!window.openid_visible_iframe) {
94 iframe.setAttribute("width", 0);
95 iframe.setAttribute("height", 0);
96 iframe.setAttribute("style", "display: none");
98 iframe.setAttribute("src", job(iframe));
99 iframe.openidBox = box;
100 box.parentNode.insertBefore(iframe, box);
101 this.frames.push(iframe);
104 this.closeFrames = function() {
105 if (this.frames.length == 0) { return false; }
106 for (var i = 0; i < this.frames.length; i++) {
107 if (this.frames[i].parentNode) { this.frames[i].parentNode.removeChild(this.frames[i]); }
109 while (this.frames.length > 0) { this.frames.pop(); }
112 this.closeFrame = function(frame) {
113 if (frame.parentNode) { frame.parentNode.removeChild(frame); }
114 var removed = this.frames.remove(frame);
115 this.onJobCompleted();
120 box.dnoi_internal.authenticationIFrames = new FrameManager(throttle);
122 box.dnoi_internal.constructButton = function(text, tooltip, onclick) {
123 var button = document.createElement('button');
124 button.textContent = text; // Mozilla
125 button.value = text; // IE
126 button.title = tooltip != null ? tooltip : '';
127 button.onclick = onclick;
128 button.style.visibility = 'hidden';
129 button.style.position = 'absolute';
130 button.style.padding = "0px";
131 button.style.fontSize = '8px';
132 button.style.top = "1px";
133 button.style.bottom = "1px";
134 button.style.right = "2px";
135 box.parentNode.appendChild(button);
139 box.dnoi_internal.constructIcon = function(imageUrl, tooltip, rightSide, visible, height) {
140 var icon = document.createElement('img');
142 icon.title = tooltip != null ? tooltip : '';
143 icon.originalTitle = icon.title;
145 icon.style.visibility = 'hidden';
147 icon.style.position = 'absolute';
148 icon.style.top = "2px";
149 icon.style.bottom = "2px"; // for FireFox (and IE7, I think)
151 icon.style.height = height; // for Chrome and IE8
154 icon.style.right = "2px";
156 icon.style.left = "2px";
158 box.parentNode.appendChild(icon);
162 box.dnoi_internal.prefetchImage = function(imageUrl) {
163 var img = document.createElement('img');
165 img.style.display = 'none';
166 box.parentNode.appendChild(img);
170 function findParentForm(element) {
171 if (element == null || element.nodeName == "FORM") {
175 return findParentForm(element.parentNode);
178 box.parentForm = findParentForm(box);
180 function findOrCreateHiddenField() {
181 var name = box.name + '_openidAuthData';
182 var existing = window.document.getElementsByName(name);
183 if (existing && existing.length > 0) {
187 var hiddenField = document.createElement('input');
188 hiddenField.setAttribute("name", name);
189 hiddenField.setAttribute("type", "hidden");
190 box.parentForm.appendChild(hiddenField);
194 box.dnoi_internal.loginButton = box.dnoi_internal.constructButton(loginButtonText, loginButtonToolTip, function() {
195 var discoveryInfo = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier];
196 if (discoveryInfo == null) {
197 trace('Ooops! Somehow the login button click event was invoked, but no openid discovery information for ' + box.lastDiscoveredIdentifier + ' is available.');
200 // The login button always sends a setup message to the first OP.
201 var selectedProvider = discoveryInfo[0];
202 selectedProvider.trySetup();
205 box.dnoi_internal.retryButton = box.dnoi_internal.constructButton(retryButtonText, retryButtonToolTip, function() {
206 box.timeout += 5000; // give the retry attempt 5s longer than the last attempt
207 box.dnoi_internal.performDiscovery(box.value);
210 box.dnoi_internal.openid_logo = box.dnoi_internal.constructIcon(openid_logo_url, null, false, true);
211 box.dnoi_internal.op_logo = box.dnoi_internal.constructIcon('', authenticatedByToolTip, false, false, "16px");
212 box.dnoi_internal.spinner = box.dnoi_internal.constructIcon(spinner_url, busyToolTip, true);
213 box.dnoi_internal.success_icon = box.dnoi_internal.constructIcon(success_icon_url, authenticatedAsToolTip, true);
214 //box.dnoi_internal.failure_icon = box.dnoi_internal.constructIcon(failure_icon_url, authenticationFailedToolTip, true);
216 // Disable the display of the DotNetOpenId logo
217 //box.dnoi_internal.dnoi_logo = box.dnoi_internal.constructIcon(dotnetopenid_logo_url);
218 box.dnoi_internal.dnoi_logo = box.dnoi_internal.openid_logo;
220 box.dnoi_internal.setVisualCue = function(state, authenticatedBy, authenticatedAs) {
221 box.dnoi_internal.openid_logo.style.visibility = 'hidden';
222 box.dnoi_internal.dnoi_logo.style.visibility = 'hidden';
223 box.dnoi_internal.op_logo.style.visibility = 'hidden';
224 box.dnoi_internal.openid_logo.title = box.dnoi_internal.openid_logo.originalTitle;
225 box.dnoi_internal.spinner.style.visibility = 'hidden';
226 box.dnoi_internal.success_icon.style.visibility = 'hidden';
227 // box.dnoi_internal.failure_icon.style.visibility = 'hidden';
228 box.dnoi_internal.loginButton.style.visibility = 'hidden';
229 box.dnoi_internal.retryButton.style.visibility = 'hidden';
231 box.dnoi_internal.state = state;
232 if (state == "discovering") {
233 box.dnoi_internal.dnoi_logo.style.visibility = 'visible';
234 box.dnoi_internal.spinner.style.visibility = 'visible';
235 box.dnoi_internal.claimedIdentifier = null;
237 window.status = "Discovering OpenID Identifier '" + box.value + "'...";
238 } else if (state == "authenticated") {
239 var opLogo = box.dnoi_internal.deriveOPFavIcon();
241 box.dnoi_internal.op_logo.src = opLogo;
242 box.dnoi_internal.op_logo.style.visibility = 'visible';
243 box.dnoi_internal.op_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
245 box.dnoi_internal.openid_logo.style.visibility = 'visible';
246 box.dnoi_internal.openid_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
248 box.dnoi_internal.success_icon.style.visibility = 'visible';
249 box.dnoi_internal.success_icon.title = box.dnoi_internal.success_icon.originalTitle.replace('{0}', authenticatedAs);
250 box.title = box.dnoi_internal.claimedIdentifier;
251 window.status = "Authenticated as " + box.value;
252 } else if (state == "setup") {
253 var opLogo = box.dnoi_internal.deriveOPFavIcon();
255 box.dnoi_internal.op_logo.src = opLogo;
256 box.dnoi_internal.op_logo.style.visibility = 'visible';
258 box.dnoi_internal.openid_logo.style.visibility = 'visible';
260 box.dnoi_internal.loginButton.style.visibility = 'visible';
261 box.dnoi_internal.claimedIdentifier = null;
262 window.status = "Authentication requires setup.";
263 } else if (state == "failed") {
264 box.dnoi_internal.openid_logo.style.visibility = 'visible';
265 //box.dnoi_internal.failure_icon.style.visibility = 'visible';
266 box.dnoi_internal.retryButton.style.visibility = 'visible';
267 box.dnoi_internal.claimedIdentifier = null;
268 window.status = authenticationFailedToolTip;
269 box.title = authenticationFailedToolTip;
270 } else if (state = '' || state == null) {
271 box.dnoi_internal.openid_logo.style.visibility = 'visible';
273 box.dnoi_internal.claimedIdentifier = null;
274 window.status = null;
276 box.dnoi_internal.claimedIdentifier = null;
277 trace('unrecognized state ' + state);
281 box.dnoi_internal.isBusy = function() {
282 return box.dnoi_internal.state == 'discovering' ||
283 box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier].busy();
286 box.dnoi_internal.canAttemptLogin = function() {
287 if (box.value.length == 0) return false;
288 if (box.dnoi_internal.authenticationRequests[box.value] == null) return false;
289 if (box.dnoi_internal.state == 'failed') return false;
293 box.dnoi_internal.getUserSuppliedIdentifierResults = function() {
294 return box.dnoi_internal.authenticationRequests[box.value];
297 box.dnoi_internal.isAuthenticated = function() {
298 return box.dnoi_internal.getUserSuppliedIdentifierResults().findSuccessfulRequest() != null;
301 box.dnoi_internal.onSubmit = function() {
302 var hiddenField = findOrCreateHiddenField();
303 if (box.dnoi_internal.isAuthenticated()) {
304 // stick the result in a hidden field so the RP can verify it
305 hiddenField.setAttribute("value", box.dnoi_internal.authenticationRequests[box.value].successAuthData);
307 hiddenField.setAttribute("value", '');
308 if (box.dnoi_internal.isBusy()) {
309 alert(loginInProgressMessage);
311 if (box.value.length > 0) {
312 // submitPending will be true if we've already tried deferring submit for a login,
313 // in which case we just want to display a box to the user.
314 if (box.dnoi_internal.submitPending || !box.dnoi_internal.canAttemptLogin()) {
315 alert(identifierRequiredMessage);
317 // The user hasn't clicked "Login" yet. We'll click login for him,
318 // after leaving a note for ourselves to automatically click submit
319 // when login is complete.
320 box.dnoi_internal.submitPending = box.dnoi_internal.submitButtonJustClicked;
321 if (box.dnoi_internal.submitPending == null) {
322 box.dnoi_internal.submitPending = true;
324 box.dnoi_internal.loginButton.onclick();
325 return false; // abort submit for now
337 /// Records which submit button caused this openid box to question whether it
338 /// was ready to submit the user's identifier so that that button can be re-invoked
339 /// automatically after authentication completes.
341 box.dnoi_internal.setLastSubmitButtonClicked = function(evt) {
346 button = evt.srcElement;
349 box.dnoi_internal.submitButtonJustClicked = button;
352 // Find all submit buttons and hook their click events so that we can validate
353 // whether we are ready for the user to postback.
354 var inputs = document.getElementsByTagName('input');
355 for (var i = 0; i < inputs.length; i++) {
357 if (el.type == 'submit') {
358 if (el.attachEvent) {
359 el.attachEvent("onclick", box.dnoi_internal.setLastSubmitButtonClicked);
361 el.addEventListener("click", box.dnoi_internal.setLastSubmitButtonClicked, true);
367 /// Returns the URL of the authenticating OP's logo so it can be displayed to the user.
369 box.dnoi_internal.deriveOPFavIcon = function() {
370 var response = box.dnoi_internal.getUserSuppliedIdentifierResults().successAuthData;
371 if (!response || response.length == 0) return;
372 var authResult = new Uri(response);
374 if (authResult.getQueryArgValue("openid.op_endpoint")) {
375 opUri = new Uri(authResult.getQueryArgValue("openid.op_endpoint"));
376 } if (authResult.getQueryArgValue("dotnetopenid.op_endpoint")) {
377 opUri = new Uri(authResult.getQueryArgValue("dotnetopenid.op_endpoint"));
378 } else if (authResult.getQueryArgValue("openid.user_setup_url")) {
379 opUri = new Uri(authResult.getQueryArgValue("openid.user_setup_url"));
381 var favicon = opUri.getAuthority() + "/favicon.ico";
385 box.dnoi_internal.createDiscoveryInfo = function(discoveryInfo, identifier) {
386 this.identifier = identifier;
387 // The claimed identifier may be null if the user provided an OP Identifier.
388 this.claimedIdentifier = discoveryInfo.claimedIdentifier;
389 trace('Discovered claimed identifier: ' + this.claimedIdentifier);
391 // Add extra tracking bits and behaviors.
392 this.findByEndpoint = function(opEndpoint) {
393 for (var i = 0; i < this.length; i++) {
394 if (this[i].endpoint == opEndpoint) {
399 this.findSuccessfulRequest = function() {
400 for (var i = 0; i < this.length; i++) {
401 if (this[i].result == authSuccess) {
406 this.busy = function() {
407 for (var i = 0; i < this.length; i++) {
408 if (this[i].busy()) {
413 this.abortAll = function() {
414 // Abort all other asynchronous authentication attempts that may be in progress.
415 box.dnoi_internal.authenticationIFrames.cancelAllWork();
416 for (var i = 0; i < this.length; i++) {
420 this.tryImmediate = function() {
421 if (this.length > 0) {
422 for (var i = 0; i < this.length; i++) {
423 box.dnoi_internal.authenticationIFrames.enqueueWork(this[i].tryImmediate);
426 box.dnoi_internal.discoveryFailed(null, this.identifier);
430 this.length = discoveryInfo.requests.length;
431 for (var i = 0; i < discoveryInfo.requests.length; i++) {
432 this[i] = new box.dnoi_internal.createTrackingRequest(discoveryInfo.requests[i], identifier);
436 box.dnoi_internal.createTrackingRequest = function(requestInfo, identifier) {
437 // It's possible during a postback that discovered request URLs are not available.
438 this.immediate = requestInfo.immediate ? new Uri(requestInfo.immediate) : null;
439 this.setup = requestInfo.setup ? new Uri(requestInfo.setup) : null;
440 this.endpoint = new Uri(requestInfo.endpoint);
441 this.identifier = identifier;
442 var self = this; // closure so that delegates have the right instance
444 this.host = self.endpoint.getHost();
446 this.getDiscoveryInfo = function() {
447 return box.dnoi_internal.authenticationRequests[self.identifier];
450 this.busy = function() {
451 return self.iframe != null || self.popup != null;
454 this.completeAttempt = function() {
455 if (!self.busy()) return false;
457 trace('iframe hosting ' + self.endpoint + ' now CLOSING.');
458 box.dnoi_internal.authenticationIFrames.closeFrame(self.iframe);
466 window.clearTimeout(self.timeout);
470 if (!self.getDiscoveryInfo().busy() && self.getDiscoveryInfo().findSuccessfulRequest() == null) {
471 trace('No asynchronous authentication attempt is in progress. Display setup view.');
472 // visual cue that auth failed
473 box.dnoi_internal.setVisualCue('setup');
479 this.authenticationTimedOut = function() {
480 if (self.completeAttempt()) {
481 trace(self.host + " timed out");
482 self.result = timedOut;
485 this.authSuccess = function(authUri) {
486 if (self.completeAttempt()) {
487 trace(self.host + " authenticated!");
488 self.result = authSuccess;
489 self.response = authUri;
490 box.dnoi_internal.authenticationRequests[self.identifier].abortAll();
493 this.authFailed = function() {
494 if (self.completeAttempt()) {
495 //trace(self.host + " failed authentication");
496 self.result = authRefused;
499 this.abort = function() {
500 if (self.completeAttempt()) {
501 trace(self.host + " aborted");
502 // leave the result as whatever it was before.
506 this.tryImmediate = function(iframe) {
507 self.abort(); // ensure no concurrent attempts
508 self.timeout = setTimeout(function() { self.authenticationTimedOut(); }, box.timeout);
509 trace('iframe hosting ' + self.endpoint + ' now OPENING.');
510 self.iframe = iframe;
511 //trace('initiating auth attempt with: ' + self.immediate);
512 return self.immediate;
514 this.trySetup = function() {
515 self.abort(); // ensure no concurrent attempts
516 window.waiting_openidBox = box;
517 self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,width=800,height=600');
521 /*****************************************
523 *****************************************/
525 /// <summary>Called to initiate discovery on some identifier.</summary>
526 box.dnoi_internal.performDiscovery = function(identifier) {
527 box.dnoi_internal.authenticationIFrames.closeFrames();
528 box.dnoi_internal.setVisualCue('discovering');
529 box.lastDiscoveredIdentifier = identifier;
530 box.dnoi_internal.discoverIdentifier(identifier, box.dnoi_internal.discoveryResult, box.dnoi_internal.discoveryFailed);
533 /// <summary>Callback that is invoked when discovery fails.</summary>
534 box.dnoi_internal.discoveryFailed = function(message, identifier) {
535 box.dnoi_internal.setVisualCue('failed');
536 if (message) { box.title = message; }
539 /// <summary>Callback that is invoked when discovery results are available.</summary>
540 /// <param name="discoveryResult">The JSON object containing the OpenID auth requests.</param>
541 /// <param name="identifier">The identifier that discovery was performed on.</param>
542 box.dnoi_internal.discoveryResult = function(discoveryResult, identifier) {
543 // Deserialize the JSON object and store the result if it was a successful discovery.
544 discoveryResult = eval('(' + discoveryResult + ')');
545 // Store the discovery results and added behavior for later use.
546 box.dnoi_internal.authenticationRequests[identifier] = discoveryBehavior = new box.dnoi_internal.createDiscoveryInfo(discoveryResult, identifier);
548 // Only act on the discovery event if we're still interested in the result.
549 // If the user already changed the identifier since discovery was initiated,
550 // we aren't interested in it any more.
551 if (identifier == box.lastDiscoveredIdentifier) {
552 discoveryBehavior.tryImmediate();
556 /// <summary>Invoked by RP web server when an authentication has completed.</summary>
557 /// <remarks>The duty of this method is to distribute the notification to the appropriate tracking object.</remarks>
558 box.dnoi_internal.processAuthorizationResult = function(resultUrl) {
559 self.waiting_openidBox = null;
560 //trace('processAuthorizationResult ' + resultUrl);
561 var resultUri = new Uri(resultUrl);
563 // Find the tracking object responsible for this request.
564 var discoveryInfo = box.dnoi_internal.authenticationRequests[resultUri.getQueryArgValue('dotnetopenid.userSuppliedIdentifier')];
565 if (discoveryInfo == null) {
566 trace('processAuthorizationResult called but no userSuppliedIdentifier parameter was found. Exiting function.');
569 var opEndpoint = resultUri.getQueryArgValue("openid.op_endpoint") ? resultUri.getQueryArgValue("openid.op_endpoint") : resultUri.getQueryArgValue("dotnetopenid.op_endpoint");
570 var tracker = discoveryInfo.findByEndpoint(opEndpoint);
571 //trace('Auth result for ' + tracker.host + ' received:\n' + resultUrl);
573 if (isAuthSuccessful(resultUri)) {
574 tracker.authSuccess(resultUri);
576 discoveryInfo.successAuthData = resultUrl;
577 var claimed_id = resultUri.getQueryArgValue("openid.claimed_id");
578 if (claimed_id && claimed_id != discoveryInfo.claimedIdentifier) {
579 discoveryInfo.claimedIdentifier = resultUri.getQueryArgValue("openid.claimed_id");
580 trace('Authenticated as ' + claimed_id);
583 // visual cue that auth was successful
584 box.dnoi_internal.claimedIdentifier = discoveryInfo.claimedIdentifier;
585 box.dnoi_internal.setVisualCue('authenticated', tracker.endpoint, discoveryInfo.claimedIdentifier);
586 if (box.dnoi_internal.onauthenticated) {
587 box.dnoi_internal.onauthenticated(box);
589 if (box.dnoi_internal.submitPending) {
590 // We submit the form BEFORE resetting the submitPending so
591 // the submit handler knows we've already tried this route.
592 if (box.dnoi_internal.submitPending == true) {
593 box.parentForm.submit();
595 box.dnoi_internal.submitPending.click();
599 tracker.authFailed();
602 box.dnoi_internal.submitPending = null;
605 function isAuthSuccessful(resultUri) {
606 if (isOpenID2Response(resultUri)) {
607 return resultUri.getQueryArgValue("openid.mode") == "id_res";
609 return resultUri.getQueryArgValue("openid.mode") == "id_res" && !resultUri.containsQueryArg("openid.user_setup_url");
613 function isOpenID2Response(resultUri) {
614 return resultUri.containsQueryArg("openid.ns");
617 box.onblur = function(event) {
618 var discoveryInfo = box.dnoi_internal.authenticationRequests[box.value];
619 if (discoveryInfo == null) {
620 if (box.value.length > 0) {
621 box.dnoi_internal.performDiscovery(box.value);
623 box.dnoi_internal.setVisualCue();
626 if ((priorSuccess = discoveryInfo.findSuccessfulRequest())) {
627 box.dnoi_internal.setVisualCue('authenticated', priorSuccess.endpoint, discoveryInfo.claimedIdentifier);
629 discoveryInfo.tryImmediate();
634 box.onkeyup = function(event) {
635 box.dnoi_internal.setVisualCue();
639 box.getClaimedIdentifier = function() { return box.dnoi_internal.claimedIdentifier; };
641 // Restore a previously achieved state (from pre-postback) if it is given.
642 var oldAuth = findOrCreateHiddenField().value;
643 if (oldAuth.length > 0) {
644 var oldAuthResult = new Uri(oldAuth);
645 // The control ensures that we ALWAYS have an OpenID 2.0-style claimed_id attribute, even against
646 // 1.0 Providers via the return_to URL mechanism.
647 var claimedId = oldAuthResult.getQueryArgValue("dotnetopenid.claimed_id");
648 var endpoint = oldAuthResult.getQueryArgValue("dotnetopenid.op_endpoint");
649 // We weren't given a full discovery history, but we can spoof this much from the
650 // authentication assertion.
651 box.dnoi_internal.authenticationRequests[box.value] = new box.dnoi_internal.createDiscoveryInfo({
652 claimedIdentifier: claimedId,
653 requests: [{ endpoint: endpoint }]
656 box.dnoi_internal.processAuthorizationResult(oldAuthResult.toString());
661 this.originalUri = url;
663 this.toString = function() {
664 return this.originalUri;
667 this.getAuthority = function() {
668 var authority = this.getScheme() + "://" + this.getHost();
672 this.getHost = function() {
673 var hostStartIdx = this.originalUri.indexOf("://") + 3;
674 var hostEndIndex = this.originalUri.indexOf("/", hostStartIdx);
675 if (hostEndIndex < 0) hostEndIndex = this.originalUri.length;
676 var host = this.originalUri.substr(hostStartIdx, hostEndIndex - hostStartIdx);
680 this.getScheme = function() {
681 var schemeStartIdx = this.indexOf("://");
682 return this.originalUri.substr(this.originalUri, schemeStartIdx);
685 this.trimFragment = function() {
686 var hashmark = this.originalUri.indexOf('#');
688 return new Uri(this.originalUri.substr(0, hashmark));
693 this.appendQueryVariable = function(name, value) {
694 var pair = encodeURI(name) + "=" + encodeURI(value);
695 if (this.originalUri.indexOf('?') >= 0) {
696 this.originalUri = this.originalUri + "&" + pair;
698 this.originalUri = this.originalUri + "?" + pair;
702 function KeyValuePair(key, value) {
707 this.Pairs = new Array();
709 var queryBeginsAt = this.originalUri.indexOf('?');
710 if (queryBeginsAt >= 0) {
711 this.queryString = url.substr(queryBeginsAt + 1);
712 var queryStringPairs = this.queryString.split('&');
714 for (var i = 0; i < queryStringPairs.length; i++) {
715 var pair = queryStringPairs[i].split('=');
716 this.Pairs.push(new KeyValuePair(unescape(pair[0]), unescape(pair[1])))
720 this.getQueryArgValue = function(key) {
721 for (var i = 0; i < this.Pairs.length; i++) {
722 if (this.Pairs[i].key == key) {
723 return this.Pairs[i].value;
728 this.containsQueryArg = function(key) {
729 return this.getQueryArgValue(key);
732 this.indexOf = function(args) {
733 return this.originalUri.indexOf(args);