1: ////////////////////////////////////////////////////////////////////////////////
     2: ////////////////////////////////////////////////////////////////////////////////
     3: //
     4: // ccc_login.js: CloudSession(.com) Control Center login program
     5: //
     6: // copyright 2010 Douglas McClendon -- dawg AT cloudsession DOT com
     7: //
     8: //
     9: // This is/was my first javascript program.  It started as literally hello
    10: // world (see EOF comments), and quickly progressed into a full fledge AJAX
    11: // login handler for CloudSession.com.
    12: //
    13: // Please feel free to send any comments or critiques to the author, as no
    14: // doubt due to his inexperience with javascript, there are many imperfections.
    15: //
    16: // Potential employers: note that this is still in early rapid development,
    17: //                      and not at all polished yet.  It is what it is.
    18: //
    19: ////////////////////////////////////////////////////////////////////////////////
    20: ////////////////////////////////////////////////////////////////////////////////
    21: 
    22: 
    23: ////////////////////////////////////////////////////////////////////////////////
    24: //
    25: // TODO
    26: //
    27: // see ccc/notes.txt
    28: //
    29: // - perhaps objectify the main mode screens.  shared virtual/inherited func
    30: //     for transitions
    31: //
    32: ////////////////////////////////////////////////////////////////////////////////
    33: 
    34: 
    35: ////////////////////////////////////////////////////////////////////////////////
    36: //
    37: // Enums
    38: //
    39: ////////////////////////////////////////////////////////////////////////////////
    40: 
    41: /* unused, found a better way, and I think there are better ways to do this
    42: ccc = new Object();
    43: 
    44: ccc.dyn_pg_states = {
    45:     "NONE" : 0,
    46:     "LOGIN" : 1,
    47:     "ABOUT" : 2,
    48:     "SIGNUP" : 3
    49:     "LOGGEDIN" : 3
    50: }
    51: */
    52: 
    53: 
    54: ////////////////////////////////////////////////////////////////////////////////
    55: //
    56: // Globals
    57: //
    58: ////////////////////////////////////////////////////////////////////////////////
    59: 
    60: // developer mode
    61: var dev = 0;
    62: // comment this out for a slower non-development experience
    63: dev = 1;
    64: 
    65: // e.g. get object of url parms
    66: //var all_url_vars = $.getUrlVars();
    67: 
    68: // this gets set if an insufficient browser version is detected
    69: var browser_bail = 0;
    70: var browser_brand = '';
    71: var browser_version = 0;
    72: 
    73: // main page state (login/about/signup/...)
    74: var ccc_dyn_pg;
    75: 
    76: // authenticated correctly yet?
    77: var logged_in = 0;
    78: 
    79: // TODO: make this a simple hash/object
    80: var logged_in_username = '';
    81: var logged_in_password = '';
    82: 
    83: // alias
    84: var body;
    85: 
    86: // seedstuffs
    87: var seed_id = -1;
    88: var seed = '';
    89: 
    90: //
    91: // rendering feature flags
    92: //
    93: 
    94: // OSX.4 not so happy with showing content right justified
    95: var use_animated_show = 1;
    96: 
    97: // 
    98: // user controllable
    99: //
   100: var bg_shown = 1;
   101: 
   102: 
   103: ////////////////////////////////////////////////////////////////////////////////
   104: //
   105: // classes
   106: //
   107: ////////////////////////////////////////////////////////////////////////////////
   108: 
   109: // note: this describes javascripts _two ways_ of implementing inheritance-
   110: //           http://www.webreference.com/js/column79/3.html
   111: //       with the second being 'more robust'
   112: //           http://www.webreference.com/js/column79/4.html
   113: 
   114: // edunote/reviewer?: References for OOP in javascript seem very confused.
   115: //                    For now, I'll just go with this-
   116: //
   117: //    function AdBox() {
   118: //    this.width = 200;
   119: //    this.height = 60;
   120: //    this.text = 'default ad text';
   121: //    this.prototype.move = function() {
   122: //	// code for move method goes here
   123: //    }
   124: //    this.prototype.display = function() {
   125: //	// code for display method goes here
   126: //    }
   127: //    } 
   128: 
   129: 
   130: ////////////////////////////////////////////////////////////////////////////////
   131: //
   132: // functions
   133: //
   134: ////////////////////////////////////////////////////////////////////////////////
   135: 
   136: 
   137: ////////////////////////////////////////////////////////////////////////////////
   138: function setup_initial_page_unveil_anim ()
   139: {
   140:    // animation timing
   141:     var ccc_main_logo_text_start_t;
   142:     var ccc_main_logo_text_anim_t;
   143:     var ccc_main_logo_anim_t;
   144:     var ccc_login_anim_t;
   145:     var ccc_all_else_wait_t;
   146:     var ccc_all_else_anim_t;
   147:     var ccc_main_logo_fadein_start_t;
   148:     var ccc_main_logo_animate_start_t;
   149:     var ccc_login_slide_start_t;
   150:     var ccc_all_else_start_t;
   151:     var ccc_all_else_done_t;
   152: 
   153:     // initially hide all of the dynamic pages
   154:     $(".ccc_dyn_pg_right").hide();
   155:     $(".ccc_dyn_pg_left").hide();
   156:     $(".ccc_dyn_pg_bottom").hide();
   157: 
   158:     // hide everything else too
   159:     $("#ccc_main_logo_text").hide();
   160:     $("#ccc_main_logo").hide();
   161:     $("#ccc_dyn_pg_filler_area").hide();
   162: 
   163: 
   164:     // use a fast mode if in development
   165:     if (dev == 1) {
   166: 	ccc_main_logo_text_start_t = 250;
   167: 	ccc_main_logo_text_anim_t = 200;
   168: 	ccc_main_logo_anim_t = 1000;
   169: 	ccc_login_anim_t = 200;
   170: 	ccc_all_else_wait_t = 123;
   171: 	ccc_all_else_anim_t = 200;
   172:     } else {
   173: 	ccc_main_logo_text_start_t = 500;
   174: 	ccc_main_logo_text_anim_t = 1000;
   175: 	ccc_main_logo_anim_t = 2000;
   176: 	ccc_login_anim_t = 1000;
   177: 	ccc_all_else_wait_t = 500;
   178: 	ccc_all_else_anim_t = 1000;
   179: 
   180:     }
   181: 
   182: 
   183:     //
   184:     // calculate animation times (for code readability)
   185:     //
   186:     ccc_main_logo_fadein_start_t = 
   187: 	ccc_main_logo_text_start_t + ccc_main_logo_text_anim_t;
   188: 
   189:     ccc_main_logo_animate_start_t = 
   190: 	ccc_main_logo_fadein_start_t + ccc_main_logo_anim_t;
   191: 
   192:     ccc_login_slide_start_t = 
   193: 	ccc_main_logo_animate_start_t + ccc_main_logo_anim_t;
   194: 
   195:     ccc_all_else_start_t = 
   196: 	ccc_login_slide_start_t + ccc_login_anim_t + ccc_all_else_wait_t;
   197: 
   198:     ccc_all_else_done_t = 
   199: 	ccc_all_else_start_t + ccc_all_else_anim_t;
   200: 	
   201:     // start at top on page reload
   202:     smooth_scroll_to_top();
   203: 
   204:     //
   205:     // set up initial animation sequence
   206:     //
   207:     setTimeout(function () {
   208: 	    if (use_animated_show) {
   209: 		$("#ccc_main_logo_text").show(ccc_main_logo_text_anim_t);
   210: 	    } else {
   211: 		$("#ccc_main_logo_text").fadeIn(ccc_main_logo_text_anim_t);
   212: 	    }
   213: 	}, ccc_main_logo_text_start_t);
   214:     setTimeout(function () {
   215: 	    $("#ccc_main_logo").fadeIn(ccc_main_logo_anim_t);
   216: 	}, ccc_main_logo_fadein_start_t);
   217:     setTimeout(function () {
   218: 	    $("#ccc_main_logo").animate({width: '20em'},ccc_main_logo_anim_t);
   219: 	}, ccc_main_logo_animate_start_t);
   220:     setTimeout(function () {
   221: 	    if (use_animated_show) {
   222: 		$("#ccc_dyn_pg_left_login").show(ccc_login_anim_t);
   223: 	    } else {
   224: 		$("#ccc_dyn_pg_left_login").fadeIn(ccc_login_anim_t);
   225: 	    }
   226: 	}, ccc_login_slide_start_t);
   227:     // note, bottom, with login button is here because otherwise the right would
   228:     //       cause it to slide down a bit more after the first slide.
   229:     setTimeout(function () {
   230: 	    if (use_animated_show) {
   231: 		$("#ccc_dyn_pg_right_login").show(ccc_all_else_anim_t);
   232: 		$("#ccc_dyn_pg_bottom_login").show(ccc_all_else_anim_t);
   233: 	    } else {
   234: 		$("#ccc_dyn_pg_right_login").fadeIn(ccc_all_else_anim_t);
   235: 		$("#ccc_dyn_pg_bottom_login").fadeIn(ccc_all_else_anim_t);
   236: 	    }
   237: 	    $('#username').focus();
   238: 	}, ccc_all_else_start_t);
   239:     setTimeout(function () {
   240: 	    $("#ccc_dyn_pg_filler_area").hide(ccc_all_else_anim_t);
   241: 	    // set dynamic page state
   242: 	    ccc_dyn_pg = ".ccc_dyn_pg_login";
   243: 	}, ccc_all_else_done_t);
   244: 
   245:     // show or hide the background, as possibly determined from user cookie
   246:     if (bg_shown == 1) {
   247: 	setTimeout(function () {$(".bgmaximage").fadeIn(3000);}, 3000);
   248:     } else {
   249: 	setTimeout(function () {$(".bgmaximage").fadeOut(3000);}, 3000);
   250:     }
   251: 
   252: 
   253: }
   254: 
   255: 
   256: ////////////////////////////////////////////////////////////////////////////////
   257: // this function taken from a comment from user edeiller on api.jquery.com page
   258: function detect_browser_version()
   259: {
   260:     var userAgent = navigator.userAgent.toLowerCase();
   261:     $.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
   262:     var version = 0;
   263: 
   264:     // Is this a version of IE?
   265:     if($.browser.msie){
   266: 	userAgent = $.browser.version;
   267: 	userAgent = userAgent.substring(0,userAgent.indexOf('.'));
   268: 	version = userAgent;
   269:     }
   270: 
   271:     // Is this a version of Chrome?
   272:     if($.browser.chrome){
   273: 	userAgent = userAgent.substring(userAgent.indexOf('chrome/') +7);
   274: 	userAgent = userAgent.substring(0,userAgent.indexOf('.'));
   275: 	version = userAgent;
   276: 	// If it is chrome then jQuery thinks it's safari so we have to tell it it isn't
   277: 	$.browser.safari = false;
   278:     }
   279: 
   280:     // Is this a version of Safari?
   281:     if($.browser.safari){
   282: 	userAgent = userAgent.substring(userAgent.indexOf('safari/') +7);
   283: 	userAgent = userAgent.substring(0,userAgent.indexOf('.'));
   284: 	version = userAgent;
   285:     }
   286: 
   287:     // Is this a version of Mozilla?
   288:     if($.browser.mozilla){
   289: 	//Is it Firefox?
   290: 	if(navigator.userAgent.toLowerCase().indexOf('firefox') != -1){
   291: 	    userAgent = userAgent.substring(userAgent.indexOf('firefox/') +8);
   292: 	    userAgent = userAgent.substring(0,userAgent.indexOf('.'));
   293: 	    version = userAgent;
   294: 	}
   295: 	// If not then it must be another Mozilla
   296: 	else{
   297: 	}
   298:     }
   299: 
   300:     // Is this a version of Opera?
   301:     if($.browser.opera){
   302: 	userAgent = userAgent.substring(userAgent.indexOf('version/') +8);
   303: 	userAgent = userAgent.substring(0,userAgent.indexOf('.'));
   304: 	version = userAgent;
   305:     }
   306:     return version;
   307: } 
   308: 
   309: 
   310: ////////////////////////////////////////////////////////////////////////////////
   311: // count words in a textelement
   312: // note: this is stripped down version of (the presumably public domain)-
   313: //       http://javascript.internet.com/forms/word-count.html
   314: function te_word_count(textelement)
   315: {
   316:     var char_count = textelement.val().length;
   317:     var fullStr = textelement.val() + " ";
   318:     var initial_whitespace_rExp = /^[^A-Za-z0-9]+/gi;
   319:     var left_trimmedStr = fullStr.replace(initial_whitespace_rExp, "");
   320:     var non_alphanumerics_rExp = rExp = /[^A-Za-z0-9]+/gi;
   321:     var cleanedStr = left_trimmedStr.replace(non_alphanumerics_rExp, " ");
   322:     var splitString = cleanedStr.split(" ");
   323:     var word_count = splitString.length - 1;
   324:     if (fullStr.length <2) {
   325: 	word_count = 0;
   326:     }
   327:     return word_count;
   328: }
   329: 
   330: 
   331: ////////////////////////////////////////////////////////////////////////////////
   332: // scroll to top of page smoothly (mostly from user glenngv at codingforums.com)
   333: var sstt_timeout;
   334: function smooth_scroll_to_top() 
   335: {
   336:     if (document.body.scrollTop != 0 || 
   337: 	document.documentElement.scrollTop != 0) {
   338: 	window.scrollBy(0,-5);
   339: 	sstt_timeout = setTimeout('smooth_scroll_to_top()', 10);
   340:     } else {
   341: 	clearTimeout(sstt_timeout);
   342:     }
   343: }
   344: 
   345: 
   346: ////////////////////////////////////////////////////////////////////////////////
   347: // switch to new dynamic page mode (login/signup/about/...)
   348: function dyn_pg_mode_switch(new_mode)
   349: {
   350:     // animation timing
   351:     var ccc_dyn_pg_pause_t;
   352:     var ccc_dyn_pg_fadein_t;
   353:     var ccc_dyn_pg_fadeout_t;
   354: 
   355: 
   356:     // use a fast mode if in development
   357:     if (dev == 1) {
   358: 	ccc_dyn_pg_pause_t = 200;
   359: 	ccc_dyn_pg_fadein_t = 500;
   360: 	ccc_dyn_pg_fadeout_t = 500;
   361:     } else {
   362: 	ccc_dyn_pg_pause_t = 300;
   363: 	ccc_dyn_pg_fadein_t = 1000;
   364: 	ccc_dyn_pg_fadeout_t = 1000;
   365:     }
   366: 
   367:     //
   368:     // do fadeout of current dynamic content, and set up fadein
   369:     //
   370:     setTimeout(function () {
   371: 	    // edunote: the focus thing works here, while document.getElementById on its own
   372: 	    //          would not, because apparently when not shown, d.gEBI returns
   373: 	    //          null, wheras this selector does the right thing in both cases.
   374: 	    //          Thus, right answer is to have one line here per mode-
   375: 		    
   376: 	    // start user at the top of the metapage
   377: 	    // jumpy
   378: 	    // scroll(0,0);
   379: 	    smooth_scroll_to_top();
   380: 
   381: 	    $(".ccc_dyn_pg_right" + ccc_dyn_pg).fadeOut(ccc_dyn_pg_fadeout_t, function () {
   382: 		    $(".ccc_dyn_pg_right" + new_mode).fadeIn(ccc_dyn_pg_fadein_t);
   383: 		});
   384: 	    $(".ccc_dyn_pg_left" + ccc_dyn_pg).fadeOut(ccc_dyn_pg_fadeout_t, function () {
   385: 		    $(".ccc_dyn_pg_left" + new_mode).fadeIn(ccc_dyn_pg_fadein_t);
   386: 		    // default focus for login
   387: 		    $('#username').focus();
   388: 		});
   389: 	    $(".ccc_dyn_pg_bottom" + ccc_dyn_pg).fadeOut(ccc_dyn_pg_fadeout_t, function () {
   390: 		    $(".ccc_dyn_pg_bottom" + new_mode).fadeIn(ccc_dyn_pg_fadein_t);
   391: 		});
   392: 	    // set the new mode, and assume (see TODO) all inputs will be blocked during
   393: 	    // transition animations.
   394: 	    ccc_dyn_pg = new_mode;
   395: 	}, ccc_dyn_pg_pause_t);
   396: }
   397: 
   398: 
   399: ////////////////////////////////////////////////////////////////////////////////
   400: function do_signed_up(xml, req_username) 
   401: {
   402:     signup_success_text = "CCC Sign-up for the new user - " + req_username +
   403: 	" - has been processed and is pending" +
   404: 	" administrator approval.  If you supplied an email address, you" +
   405: 	" will receive confirmation when your account has been activated." +
   406: 	" Thank you, have an excellent day.";
   407:     document.getElementById('ccc_signup_success_dialog').innerHTML =
   408: 	signup_success_text;
   409:     $('#ccc_signup_success_dialog').dialog({ 
   410: 	    width: 600,
   411: 	    close: 
   412: 	    function (evenut,ui) {
   413: 		// clear the signup form
   414: 		signup_req_clear_button_handler();
   415: 		// switch to the about page
   416: 		dyn_pg_mode_switch(".ccc_dyn_pg_about");
   417: 	    }
   418: 	});
   419: }
   420: 
   421: 
   422: ////////////////////////////////////////////////////////////////////////////////
   423: function do_log_on(xml) 
   424: {
   425:     var loggedin_div;
   426: 
   427:     logged_in = 1;
   428:     logged_in_username = document.getElementById('username').value;
   429:     logged_in_password = document.getElementById('password').value;
   430: 
   431:     clear_userpass();
   432:     $('#username').attr('disabled', true);
   433:     $('#password').attr('disabled', true);
   434:     
   435: 
   436:     document.getElementById('ccc_login_status').innerHTML =
   437: 	"logged in";
   438:     $('#ccc_login_server_response').hide('fast');
   439: 
   440:     /*
   441:     // old reference: how I first played with tabs in js only (!php/ajax)
   442:     newtabs = document.createElement('div');
   443:     newtabs.setAttribute('id', 'loggedin_tabs');
   444: 
   445:     tablist = document.createElement('ul');
   446:     newtabs.appendChild(tablist);
   447: 
   448:     tab_one = document.createElement('li');
   449:     tablist.appendChild(tab_one);
   450:     tab_one_link = document.createElement('a'); 
   451:     tab_one_link.setAttribute('href', '#loggedin_tabs-1');
   452:     tab_one.appendChild(tab_one_link);
   453:     tab_one_link_text = document.createTextNode('something'); 
   454:     tab_one_link.appendChild(tab_one_link_text);
   455: 
   456:     tab_one_div = document.createElement('div');
   457:     newtabs.appendChild(tab_one_div);
   458:     tab_one_div.setAttribute('id', 'loggedin_tabs-1');
   459:     tab_one_content = document.createElement('p');
   460:     tab_one_div.appendChild(tab_one_content);
   461:     tab_one_content_text = document.createTextNode('some text');
   462:     tab_one_content.appendChild(tab_one_content_text);
   463: 
   464:     tab_two = document.createElement('li');
   465:     tablist.appendChild(tab_two);
   466:     tab_two_link = document.createElement('a'); 
   467:     tab_two_link.setAttribute('href', '#loggedin_tabs-2');
   468:     tab_two.appendChild(tab_two_link);
   469:     tab_two_link_text = document.createTextNode('someotherthing'); 
   470:     tab_two_link.appendChild(tab_two_link_text);
   471: 
   472:     tab_two_div = document.createElement('div');
   473:     newtabs.appendChild(tab_two_div);
   474:     tab_two_div.setAttribute('id', 'loggedin_tabs-2');
   475:     tab_two_content = document.createElement('p');
   476:     tab_two_div.appendChild(tab_two_content);
   477:     tab_two_content_text = document.createTextNode('some other text');
   478:     tab_two_content.appendChild(tab_two_content_text);
   479:     
   480:     document.getElementById('ccc_dyn_pg_left_loggedin').appendChild(newtabs);
   481:     */
   482: 
   483: ////////////////////////////////////////////////////////////////////////////////
   484: // note: major: This is, gross ugly and hackish _looking_.  The first cleanup 
   485: //       might be to use a seperate ajax response that only returns the
   486: //       renderable x/ht/ml, ... but actually, I think I'd still have to
   487: //       use these replaces (which could probably be made prettier).
   488: //       The real solution, seems to be elucidated(word?) in this article,
   489: //       and I was happy that it actually reassures me that the painful
   490: //       brute force 'right' method that I had been coming to in my mind,
   491: //       turns out to actually be the correct one.  Though I still need to
   492: //       dig a few links to see a good example of such an xml walkthru
   493: //       cloning.
   494: //
   495: //       http://slayeroffice.com/articles/innerHTML_alternatives/#6a
   496: //
   497: // note: also todo: try $.load(), described as simply ajax loading a div,
   498: //       though not appropriate here, as I want the content to not be available
   499: //       to unauthenticated users.  (but I'll no doubt learn more by using it)
   500: ////////////////////////////////////////////////////////////////////////////////
   501: 
   502:     // note: ]]> in place of the final replace's ... will work, but messes with
   503:     //       emac's syntax recognition
   504:     document.getElementById('ccc_dyn_pg_left_loggedin').innerHTML =
   505:     	$('loggedin_xml', xml).text().
   506:     	replace(/^.*xml.version......../g, '').replace(/...$/g, '');
   507: 
   508: 
   509:     $("#loggedin_tabs").tabs();
   510:     $("#loggedin_tabs").tabs('select', '#loggedin_tabs-control');
   511:   
   512:     dyn_pg_mode_switch(".ccc_dyn_pg_loggedin");
   513: }
   514: 
   515: 
   516: ////////////////////////////////////////////////////////////////////////////////
   517: //
   518: function do_log_out(xml) 
   519: {
   520:     logged_in = 0;
   521:     clear_userpass();
   522:     logged_in_username = '';
   523:     logged_in_password = '';
   524: 
   525:     $('#username').removeAttr('disabled');
   526:     $('#password').removeAttr('disabled');
   527:     
   528: 
   529:     document.getElementById('ccc_login_status').innerHTML =
   530: 	"logged out";
   531:     document.getElementById('ccc_login_server_response').innerHTML =
   532: 	"cloudsession user logged out";
   533: 
   534:     dyn_pg_mode_switch(".ccc_dyn_pg_login");
   535: }
   536: 
   537: 
   538: ////////////////////////////////////////////////////////////////////////////////
   539: // handle a server authentication request response
   540: function seed_response_handler(xml)
   541: {
   542: 
   543:     if ($('seed_id', xml).text() == '') {
   544: 	document.getElementById('ccc_login_server_response').innerHTML =
   545: 	    "server failed to acknowledge login request, please try again";
   546: 	return;
   547:     }
   548: 
   549:     hashed_password = $.sha256($.sha256($("input[name=password]")) + 
   550:     			   $('seed', xml).text());
   551: 
   552:     $.post("cgi/ccc_login.php", {
   553: 	    command: "validate-login",
   554: 		username: $("input[name=username]").val(),
   555: 		hashed_password: hashed_password,
   556: 		seed_id: $('seed_id', xml).text()
   557: 		}, function (xml) {auth_response_handler(xml);});
   558: 
   559:     /* why does this not work when commenting out the post above??
   560:     document.getElementById('ccc_login_server_response').innerHTML =
   561: 	"posted:: " + "username: " + $("input[name=username]").val() +
   562: 	"hashed_password: " + hashed_password + "seed_id: " + $('seed_id', xml).text();
   563:     document.getElementById('ccc_login_server_response').show('fast');
   564:     */
   565: 
   566:     // TODO: need busy indicator and 7(+?)s timeout here 
   567: }
   568: 
   569: 
   570: ////////////////////////////////////////////////////////////////////////////////
   571: // handle a server authentication request response
   572: function auth_response_handler(xml)
   573: {
   574:     if ( $("validate_login_result", xml).text().match(/valid login/g) ) {
   575: 	do_log_on(xml);
   576:     } else {
   577: 	$('#ccc_login_server_response').show('fast');
   578: 	document.getElementById('ccc_login_status').innerHTML =
   579: 	    "login failed, not logged in";
   580: 	if ( $("server_error", xml).text() != "" ) {
   581: 	    document.getElementById('ccc_login_server_response').innerHTML =
   582: 		$("server_error", xml).text() + ",<br/>please try again";
   583: 	}
   584: 	if ( $("validate_login_result", xml).text() != "" ) {
   585: 	    document.getElementById('ccc_login_server_response').innerHTML =
   586: 		$("validate_login_result", xml).text() + ",<br/>please try again";
   587: 	}
   588:     }
   589: }
   590: 
   591: 
   592: ////////////////////////////////////////////////////////////////////////////////
   593: // handle a server authentication request response
   594: function signup_response_handler(xml, req_username)
   595: {
   596:     if ( $("validate_signup_result", xml).text().match(/valid signup/g) ) {
   597: 	do_signed_up(xml, req_username);
   598:     } else {
   599: 	signup_response_text = '';
   600: 	signup_response_text += "CCC Sign-up failed.<br/><br/>";
   601: 	if ( $("server_error", xml).text() != "" ) {
   602: 	    signup_response_text += 
   603: 		$("server_error", xml).text() + ",<br/>please try again";
   604: 	} else if ( $("validate_signup_result", xml).text() != "" ) {
   605: 	    signup_response_text += 
   606: 		$("validate_signup_result", xml).text() + ",<br/>please try again";
   607: 	}
   608: 	document.getElementById('ccc_signup_server_error_dialog').innerHTML =
   609: 	    signup_response_text;
   610: 	$('#ccc_signup_server_error_dialog').dialog({ width: 600 });
   611:     }
   612: }
   613: 
   614: 
   615: ////////////////////////////////////////////////////////////////////////////////
   616: // check if any of the dynamic page mode switch animations are active
   617: function check_for_animation() {
   618:     // this should work (maybe more correct all selector syntax is needed)
   619:     //    if ($(':animated').length) {return true;}
   620: 
   621:     // this does work
   622:     if ($('.ccc_dyn_pg_right:animated').length) {return true;}
   623:     if ($('.ccc_dyn_pg_left:animated').length) {return true;}
   624:     if ($('.ccc_dyn_pg_bottom:animated').length) {return true;}
   625:     return false;
   626: }
   627: 
   628: 
   629: ////////////////////////////////////////////////////////////////////////////////
   630: function clear_userpass()
   631: {
   632:     document.getElementById('username').value = '';
   633:     document.getElementById('password').value = '';
   634:     // reviewer: why don't these work the same? 
   635:     //    $('#username').value = '';
   636:     //    $('#password').value = '';
   637:     // hide any server response
   638:     $('#ccc_login_server_response').hide();
   639:     $('#username').focus();
   640:     
   641: }
   642: 
   643: 
   644: ////////////////////////////////////////////////////////////////////////////////
   645: //
   646: // button press event handlers
   647: //
   648: 
   649: 
   650: ////////////////////////////////////////////////////////////////////////////////
   651: function login_attempt_button_handler() 
   652: {
   653:     // if an animation is in progress, ignore the click
   654:     if (check_for_animation()) {return false;}
   655: 
   656:     // reviewer question: jquery .html no work with xml???
   657:     //$("#ccc_login_status_text").html = "logging in...";
   658:     document.getElementById('ccc_login_status').innerHTML =
   659: 	"logging in...";
   660: 
   661:     // first we get a seed, then the seed_response_handler will
   662:     // handle the rest of the process
   663:     $.post("cgi/ccc_login.php", {
   664: 	    command: "get-seed",
   665: 		}, function (xml) {seed_response_handler(xml);});
   666: }
   667: 
   668: 
   669: ////////////////////////////////////////////////////////////////////////////////
   670: function login_clear_button_handler() 
   671: {
   672:     if (check_for_animation()) {return false;}
   673: 
   674:     clear_userpass();
   675: }
   676: 
   677: 
   678: ////////////////////////////////////////////////////////////////////////////////
   679: function login_about_button_handler() 
   680: {
   681:     // if an animation is in progress, ignore the click
   682:     if (check_for_animation()) {return false;}
   683: 
   684:     dyn_pg_mode_switch(".ccc_dyn_pg_about");
   685: }
   686: 
   687: 
   688: ////////////////////////////////////////////////////////////////////////////////
   689: function login_signup_button_handler() 
   690: {
   691:     // if an animation is in progress, ignore the click
   692:     if (check_for_animation()) {return false;}
   693: 
   694:     dyn_pg_mode_switch(".ccc_dyn_pg_signup");
   695: }
   696: 
   697: 
   698: ////////////////////////////////////////////////////////////////////////////////
   699: function login_gotologin_button_handler() 
   700: {
   701:     // if an animation is in progress, ignore the click
   702:     if (check_for_animation()) {return false;}
   703: 
   704:     dyn_pg_mode_switch(".ccc_dyn_pg_login");
   705: }
   706: 
   707: 
   708: ////////////////////////////////////////////////////////////////////////////////
   709: function logout_button_handler() 
   710: {
   711:     // if an animation is in progress, ignore the click
   712:     if (check_for_animation()) {return false;}
   713: 
   714:     do_log_out();
   715: }
   716: 
   717: 
   718: ////////////////////////////////////////////////////////////////////////////////
   719: function help_button_handler() 
   720: {
   721:     // if an animation is in progress, ignore the click
   722:     if (check_for_animation()) {return false;}
   723: 
   724:     $('#ccc_help_dialog').dialog();
   725: 
   726:     /* even on ff35, not so smooth, but perhaps next year
   727:     $('#ccc_help_dialog').dialog({
   728: 	    show: "slide",
   729: 	    hide: "slide",
   730: 		});
   731:     */
   732: }
   733: 
   734: 
   735: ////////////////////////////////////////////////////////////////////////////////
   736: function signup_req_send_button_handler() 
   737: {
   738:     var req_hashed_password = '';
   739: 
   740: 
   741:     // if an animation is in progress, ignore the click
   742:     if (check_for_animation()) {return false;}
   743: 
   744:     if ($('#req_password').val() != $('#req_password_vfy').val()) {
   745: 	$('#ccc_signup_req_pwnomatch_dialog').dialog();
   746: 	return false;
   747:     }
   748: 
   749:     if (!$('#req_password').val().match(/^[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]+/)) {
   750: 	$('#ccc_signup_req_alphanum_dialog').dialog();
   751: 	return false;
   752:     } 
   753: 
   754:     req_username = $("input[name=req_username]").val();
   755:     req_hashed_password = $.sha256($("input[name=req_password]"));
   756:  
   757:     $.post("cgi/ccc_login.php", {
   758: 	    command: "validate-signup",
   759: 		req_username: req_username,
   760: 		req_hashed_password: req_hashed_password,
   761: 		req_sapcha: document.getElementById('req_sapcha').value
   762: 		}, 
   763: 	function (xml) {
   764: 	    signup_response_handler(xml, req_username);
   765: 	}
   766: 	);
   767: 
   768:     // TODO: need busy indicator and 7(+?)s timeout here 
   769: }
   770: 
   771: 
   772: ////////////////////////////////////////////////////////////////////////////////
   773: function signup_req_clear_button_handler() 
   774: {
   775:     // if an animation is in progress, ignore the click
   776:     if (check_for_animation()) {return false;}
   777: 
   778:     document.getElementById('req_username').value = '';
   779:     document.getElementById('req_password').value = '';
   780:     document.getElementById('req_password_vfy').value = '';
   781:     document.getElementById('req_sapcha').value = '';
   782: 
   783:     // reset the wordcount
   784:     req_sapcha_keyup_handler();
   785: }
   786: 
   787: 
   788: ////////////////////////////////////////////////////////////////////////////////
   789: function toggle_bg_button_handler() 
   790: {
   791:     // if an animation is in progress, ignore the click
   792:     if (check_for_animation()) {return false;}
   793: 
   794:     if (bg_shown == 1) {
   795: 	$(".bgmaximage").fadeOut('slow');
   796: 	$.cookie('ccc_background_pref',
   797: 		 null,
   798: 		 { path: '/', expires: 369 });
   799: 	$.cookie('ccc_background_pref',
   800: 		 '0',
   801: 		 { path: '/', expires: 369 });
   802: 	bg_shown = 0;
   803:     } else {
   804: 	$(".bgmaximage").fadeIn('slow');
   805: 	$.cookie('ccc_background_pref',
   806: 		 null,
   807: 		 { path: '/', expires: 369 });
   808: 	$.cookie('ccc_background_pref',
   809: 		 '1',
   810: 		 { path: '/', expires: 369 });
   811: 	bg_shown = 1;
   812:     }
   813: 
   814: }
   815: 
   816: 
   817: ////////////////////////////////////////////////////////////////////////////////
   818: //
   819: // text input keyup handlers
   820: //
   821: 
   822: 
   823: ////////////////////////////////////////////////////////////////////////////////
   824: function req_username_keyup_handler() 
   825: {
   826:     if ($('#req_username').val().length > 2) {
   827: 	$('#req_username_advice_text').css('color', '#007700');
   828:     } else {
   829: 	$('#req_username_advice_text').css('color', '#770000');
   830:     }
   831: }
   832: 
   833: 
   834: ////////////////////////////////////////////////////////////////////////////////
   835: function req_password_keyup_handler() 
   836: {
   837: 
   838:     // technically the input box should prevent > 42 as well
   839:     if (($('#req_password').val().length < 3) || 
   840: 	($('#req_password_vfy').val().length < 3) || 
   841: 	($('#req_password').val().length > 42) || 
   842: 	($('#req_password_vfy').val().length > 42)) {
   843: 	$('#req_password_length_text').css('color', '#770000');
   844:     } else {
   845: 	$('#req_password_length_text').css('color', '#007700');
   846:     }
   847: 
   848:     // now check for match
   849:     if ($('#req_password').val() != $('#req_password_vfy').val()) {
   850: 	document.getElementById('req_password_match_text').innerHTML =
   851: 	    "* Password and verify-pw do not match.";
   852: 	$('#req_password_match_text').css('color', '#770000');
   853:     } else {
   854: 	document.getElementById('req_password_match_text').innerHTML =
   855: 	    "* Password and verify-pw match.";
   856: 	$('#req_password_match_text').css('color', '#007700');
   857:     }
   858: }
   859: 
   860: 
   861: ////////////////////////////////////////////////////////////////////////////////
   862: function req_sapcha_keyup_handler() 
   863: {
   864:     word_count = te_word_count($('#req_sapcha'));
   865:     document.getElementById('req_sapcha_advice_text').innerHTML = 
   866: 	'word count :: ' + word_count;
   867:     if (word_count > 41) {
   868: 	$('#req_sapcha_advice_text').css('color', '#007700');
   869:     } else {
   870: 	$('#req_sapcha_advice_text').css('color', '#770000');
   871:     }
   872: }
   873: 
   874: ////////////////////////////////////////////////////////////////////////////////
   875: //
   876: // main
   877: //
   878: ////////////////////////////////////////////////////////////////////////////////
   879: 
   880: 
   881: ////////////////////////////////////////////////////////////////////////////////
   882: //
   883: function ccc_login_main() 
   884: {
   885: 
   886:     // TODO: move all but first sentence to an in-html div
   887:     if (browser_bail) {
   888: 	$(".bgmaximage").hide();
   889: 	$("#ccc_total_area").hide();
   890: 	document.getElementById('ccc_browser_bail_area').innerHTML =
   891: 	    "<br/><br/>Your browser- " + browser_brand + " ( version " + 
   892: 	    browser_version + " ) may be insufficient to render" + 
   893: 	    " the CloudSession Control Center demo.<br/><br/>Please visit" +
   894: 	    " mozilla.org to download and use the latest version of" + 
   895: 	    " Firefox.  Or send an email to Dawg AT CloudSession DOT com," +
   896: 	    " to get the detection code fixed if this blocking is" +
   897: 	    " unnecessary.  Alternately, you can use this url-" +
   898: 	    " <a href='http://cloudsession.com/login/?no_browser_check>" +
   899: 	    "http://cloudsession.com/login/?no_browser_check</a>.";
   900: 	return;
   901:     }
   902: 
   903:     // init the main application state/mode
   904:     ccc_dyn_pg = ".ccc_dyn_pg_none";
   905: 
   906:     // a useful alias
   907:     body = document.getElementById('body');
   908: 
   909:     // clear the form inputs once on main page load
   910:     document.getElementById('username').value = '';
   911:     document.getElementById('password').value = '';
   912:     document.getElementById('req_username').value = '';
   913:     document.getElementById('req_password').value = '';
   914:     document.getElementById('req_sapcha').value = '';
   915: 
   916:     // init advice defaults 
   917:     req_username_keyup_handler();
   918:     req_password_keyup_handler();
   919:     req_sapcha_keyup_handler();
   920:     
   921: 
   922:     // not so happy yet (simple temp test rig)
   923:     document.getElementById('ccc_login_status').innerHTML =
   924: 	"not logged in";
   925: 
   926:     // it seems wrong, but a page reload doesn't seem to clear these
   927:     $('#username').removeAttr('disabled');
   928:     $('#password').removeAttr('disabled');
   929: 
   930: 
   931:     //disable debug info
   932:     $('#ccc_login_status').hide();
   933: 
   934:     // initially hide server response that hasn't been received yet
   935:     $('#ccc_login_server_response').hide('fast');
   936: 
   937:     //
   938:     // setup button event handlers
   939:     //
   940: 
   941:     // edunote: wrapping arg in "function () {X()}" makes it a funcptr.
   942:     $("#ccc_button_login_attempt").click(function () {
   943: 	    login_attempt_button_handler();
   944: 	});
   945:     $("#ccc_button_login_clear").click(function () {
   946: 	    login_clear_button_handler();
   947: 	});
   948:     // note: these are classes because they exist in more than one page
   949:     $(".ccc_button_about").click(function () {
   950: 	    login_about_button_handler();
   951: 	});
   952:     $(".ccc_button_signup").click(function () {
   953: 	    login_signup_button_handler();
   954: 	});
   955:     $(".ccc_button_gotologin").click(function () {
   956: 	    login_gotologin_button_handler();
   957: 	});
   958:     $(".ccc_button_logout").click(function () {
   959: 	    logout_button_handler();
   960: 	});
   961:     $(".ccc_button_help").click(function () {
   962: 	    help_button_handler();
   963: 	});
   964:     $(".ccc_button_signup_req_send").click(function () {
   965: 	    signup_req_send_button_handler();
   966: 	});
   967:     $(".ccc_button_signup_req_clear").click(function () {
   968: 	    signup_req_clear_button_handler();
   969: 	});
   970:     $("#ccc_button_toggle_bg").click(function () {
   971: 	    toggle_bg_button_handler();
   972: 	});
   973: 
   974:     //
   975:     // setup text input keypress handlers (username/password advise)
   976:     //
   977:     $("#req_username").keyup(function () {
   978: 	    req_username_keyup_handler();
   979: 	});
   980:     $("#req_password").keyup(function () {
   981: 	    req_password_keyup_handler();
   982: 	});
   983:     $("#req_password_vfy").keyup(function () {
   984: 	    req_password_keyup_handler();
   985: 	});
   986:     $("#req_sapcha").keyup(function () {
   987: 	    req_sapcha_keyup_handler();
   988: 	});
   989: 
   990:     //
   991:     // various UI features init
   992:     //
   993: 
   994:     // add spiffy jqui/ui-state-hover button hovering to ccc_button(s)
   995:     // edunote::js:: apparently this kind of line breaking works
   996:     $(".ccc_button:not(.ui-state-disabled)")
   997: 	.hover(function(){ $(this).addClass("ui-state-hover"); },
   998: 	       function(){ $(this).removeClass("ui-state-hover"); });
   999: 
  1000:     // for maximage bg scaling plugin
  1001:     jQuery('img.bgmaximage').maxImage({
  1002: 	    isBackground: true,
  1003: 		overflow: 'auto'
  1004: 		});
  1005: 
  1006:     //
  1007:     // setup initial page unvel animation
  1008:     //
  1009: 
  1010:     setup_initial_page_unveil_anim();
  1011: 
  1012: 
  1013: } // end ccc_login_main
  1014: 
  1015: 
  1016: ////////////////////////////////////////////////////////////////////////////////
  1017: // insecure bounces to secure
  1018: 
  1019: if (window.location.hostname != 'localhost') {
  1020:     if (window.location.protocol != 'https:') {
  1021: 	window.open('https://ssl.perfora.net/secure.cloudsession.com/login/',
  1022: 		    '_self');
  1023:     }
  1024: }
  1025: 
  1026: ////////////////////////////////////////////////////////////////////////////////
  1027: // browser bail handling
  1028: 
  1029: // let the user opt out of browser unsupport
  1030: if (!('no_browser_check' in $.get_url_vars())) {
  1031:     browser_version = detect_browser_version();
  1032:     if ($.browser.mozilla) {
  1033: 	browser_brand = 'Mozilla/Firefox';
  1034: 	if (browser_version < 3) {
  1035: 	    browser_bail = 1;
  1036: 	} else {
  1037: 	}
  1038:     } else if ($.browser.msie) {
  1039: 	browser_brand = 'Internet/Explorer';
  1040: 	if (browser_version < 8) {
  1041: 	    browser_bail = 1;
  1042: 	} else {
  1043: 	    // no point in dev for IE
  1044: 	    dev = 0;
  1045: 	}
  1046:     } else if ($.browser.webkit) {
  1047: 	browser_brand = 'Webkit/Safari';
  1048: 	if (browser_version < 531) {
  1049: 	    browser_bail = 1;
  1050: 	} else {
  1051: 	    // no point in dev for safari
  1052: 	    dev = 0;
  1053: 	    // does not work so well
  1054: 	    use_animated_show = 0;
  1055: 	}
  1056:     } else {
  1057: 	browser_brand = 'Unknown/Opera';
  1058: 	browser_bail = 1;
  1059:     }
  1060: }
  1061: 
  1062: // manual testing hook
  1063: if ('use_animated_show' in $.get_url_vars()) {
  1064:     use_animated_show = $.get_url_vars['use_animated_show'];
  1065: }
  1066: 
  1067: // check for background disable cookie
  1068: if ($.cookie('ccc_background_pref') == null) {
  1069:     bg_shown = 1;
  1070: } else {
  1071:     bg_shown = $.cookie('ccc_background_pref');
  1072: }
  1073:     
  1074: 
  1075: ////////////////////////////////////////////////////////////////////////////////
  1076: // 'run' main (when DOM is fully loaded, I think this means)
  1077: jQuery(document).ready(function () {ccc_login_main();});
  1078: 
  1079: 
  1080: ////////////////////////////////////////////////////////////////////////////////
  1081: //
  1082: // end code (comments below)
  1083: //
  1084: ////////////////////////////////////////////////////////////////////////////////
  1085: 
  1086: //
  1087: // The first javascript style guide I found.  Best and winner thus far...
  1088: //
  1089: // http://javascript.crockford.com/code.html
  1090: //
  1091: // and longterm security todo- top ten web application issues from owasp
  1092: //
  1093: // http://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project
  1094: // 
  1095: 
  1096: // // edu: historical
  1097: //    Hello World: clicks on links cause graffiti alert	
  1098: //    $("a").click(function() {
  1099: //	    alert("DMC wuz here!");
  1100: //	});
  1101: 
  1102: // edu: todo: need a good use of an object as associative array...
  1103: // http://www.quirksmode.org/js/associative.html
  1104: // http://www.mojavelinux.com/articles/javascript_hashes.html
  1105: 
  1106: ////////////////////////////////////////////////////////////////////////////////
  1107: //
  1108: // EOF
  1109: //
  1110: ////////////////////////////////////////////////////////////////////////////////
  1111: ////////////////////////////////////////////////////////////////////////////////
  1112: