/* Author: Jacob Gable, May 2011. License: Ms-PL; No Attribution Required; Other licenses retained where applicable... */ // Do this first so we can minimize funky textbox display; function updateWidths() { // page width - 10 for left offset; var newWidth = $('.page').width() - 10; $('footer, header').width(newWidth); }; updateWidths(); // hack for fixing the width when resizing the browser. $(window).resize(function() { updateWidths(); }); // usage: log('inside coolFunc', this, arguments); // paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ window.log = function() { log.history = log.history || []; // store logs to an array for reference log.history.push(arguments); if (this.console) console.log(Array.prototype.slice.call(arguments)); }; // Placeholder support for IE9 if (!('placeholder' in document.createElement('input'))) { $('[placeholder]').each(function() { var self = $(this); var p = 'placeholder'; self.data('empty', self.val() == ''); self.focus(function() { if (self.data('empty') && self.val() == self.attr(p)) { self.val('').removeClass(p); } }).blur(function() { if (self.data('empty')) { self.val(self.attr(p)).addClass(p); } }).change(function() { self.data('empty', self.val() == ''); }); if (self.val() == '' || self.val() == self.attr(p)) { self.val(self.attr(p)).addClass(p); } }); $('form:has([placeholder])').bind('submit', function() { $(this).find('[placeholder]').each(function() { if ($(this).data('empty')) { $(this).val(''); } }); }); } /** * jQuery.ScrollTo - Easy element scrolling using jQuery. * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com * Dual licensed under MIT and GPL. * Date: 3/9/2009 * @author Ariel Flesler * @version 1.4.1 * * http://flesler.blogspot.com/2007/10/jqueryscrollto.html */ ; (function($) { var m = $.scrollTo = function(b, h, f) { $(window).scrollTo(b, h, f) }; m.defaults = { axis: 'xy', duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1 }; m.window = function(b) { return $(window).scrollable() }; $.fn.scrollable = function() { return this.map(function() { var b = this, h = !b.nodeName || $.inArray(b.nodeName.toLowerCase(), ['iframe', '#document', 'html', 'body']) != -1; if (!h) return b; var f = (b.contentWindow || b).document || b.ownerDocument || b; return $.browser.safari || f.compatMode == 'BackCompat' ? f.body : f.documentElement }) }; $.fn.scrollTo = function(l, j, a) { if (typeof j == 'object') { a = j; j = 0 } if (typeof a == 'function') a = { onAfter: a }; if (l == 'max') l = 9e9; a = $.extend({}, m.defaults, a); j = j || a.speed || a.duration; a.queue = a.queue && a.axis.length > 1; if (a.queue) j /= 2; a.offset = n(a.offset); a.over = n(a.over); return this.scrollable().each(function() { var k = this, o = $(k), d = l, p, g = {}, q = o.is('html,body'); switch (typeof d) { case 'number': case 'string': if (/^([+-]=)?\d+(\.\d+)?(px)?$/.test(d)) { d = n(d); break } d = $(d, this); case 'object': if (d.is || d.style) p = (d = $(d)).offset() } $.each(a.axis.split(''), function(b, h) { var f = h == 'x' ? 'Left' : 'Top', i = f.toLowerCase(), c = 'scroll' + f, r = k[c], s = h == 'x' ? 'Width' : 'Height'; if (p) { g[c] = p[i] + (q ? 0 : r - o.offset()[i]); if (a.margin) { g[c] -= parseInt(d.css('margin' + f)) || 0; g[c] -= parseInt(d.css('border' + f + 'Width')) || 0 } g[c] += a.offset[i] || 0; if (a.over[i]) g[c] += d[s.toLowerCase()]() * a.over[i] } else g[c] = d[i]; if (/^\d+$/.test(g[c])) g[c] = g[c] <= 0 ? 0 : Math.min(g[c], u(s)); if (!b && a.queue) { if (r != g[c]) t(a.onAfterFirst); delete g[c] } }); t(a.onAfter); function t(b) { o.animate(g, j, a.easing, b && function() { b.call(this, l, a) }) }; function u(b) { var h = 'scroll' + b; if (!q) return k[h]; var f = 'client' + b, i = k.ownerDocument.documentElement, c = k.ownerDocument.body; return Math.max(i[h], c[h]) - Math.min(i[f], c[f]) } }).end() }; function n(b) { return typeof b == 'object' ? b : { top: b, left: b } } })(jQuery); $.fn.setCursorPosition = function(pos) { this.each(function(index, elem) { if (elem.setSelectionRange) { elem.setSelectionRange(pos, pos); } else if (elem.createTextRange) { var range = elem.createTextRange(); range.collapse(true); range.moveEnd('character', pos); range.moveStart('character', pos); range.select(); } }); return this; }; function getTimeString() { var currentTime = new Date() var hours = currentTime.getHours() var minutes = currentTime.getMinutes() var suffix = "AM"; if (hours >= 12) { suffix = "PM"; hours = hours - 12; } if (hours == 0) { hours = 12; } if (minutes < 10) minutes = "0" + minutes return hours + ":" + minutes + " " + suffix; }; <!-- Our Web Chat JS, at bottom for faster loading --> function webChat(elem, textElem, submitBtn) { this.polling = false; this.pollSeconds = 10; this.chatRoot = $(elem); this.dlg = $('<dialog></dialog>'); // Add the dialog element. this.chatRoot.append(this.dlg); this.chatTextBox = $(textElem); this.submitBtn = $(submitBtn); var self = this; this.submitBtn.click(function() { self.submitMessage(); }); this.chatTextBox.keypress(function(e) { // Submit on enter; if (e.keyCode == 13) { self.submitMessage(); self.chatTextBox.setCursorPosition(0); e.preventDefault(); } }); }; webChat.prototype.submitMessage = function() { this.sendMessage($(this.chatTextBox).val()); $(this.chatTextBox).val(''); }; webChat.prototype.addReply = function(message) { var sender = 'Expert'; // TODO: show this at some point? var dt = $('<dt class="chatTitle reply">' + sender + '</dt>'); var dd = $('<dd class="chatMsg reply"><p>' + message + '</p><span>' + getTimeString() + '</span><div class="arrow"></div><div class="arrow arrowOver"></div></dd>'); this.dlg.append(dt); this.dlg.append(dd); $(dd).fadeIn(); $.scrollTo($('.page').height(), 400); }; webChat.prototype.sendMessage = function(message) { // TODO: sanitize input? var sender = 'Me'; var dt = $('<dt class="chatTitle">' + sender + '</dt>'); var dd = $('<dd class="chatMsg"><p>' + message + '</p><span>' + getTimeString() + '</span><div class="arrow"></div><div class="arrow arrowOver"></div></dd>'); this.dlg.append(dt); this.dlg.append(dd); $(dd).fadeIn(); $.scrollTo($('.page').height(), 400); }; // A simple chat bot for adding replies. function chatBot(webChatObj, messages) { if (messages == undefined) { messages = [ "What's up dude?", "For realz?", "She said what?", "Can I get an Amen?", "Oh no he didn't", "u r so funny", ]; } this.wc = webChatObj; this.messages = messages; }; chatBot.prototype.start = function(secs) { if (secs == undefined) { secs = 10; } this.currMsgIdx = 0; this.started = true; var self = this; this.intervalId = setInterval(function() { self.wc.addReply(self.messages[self.currMsgIdx % self.messages.length]); self.currMsgIdx++; }, secs * 1000); } chatBot.prototype.stop = function() { if (this.intervalId != undefined) { clearInterval(this.intervalId); } this.started = false; } // Get our web chat log inserted into the DOM. var chat = new webChat($('#chatLog'), '.chatText', '#submitBtn'); // Start our chatty bot. var bot = new chatBot(chat); bot.start(); function toggleBot() { if (bot.started) { bot.stop(); } else { bot.start(); } }
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Web Chat</title> </head> <body> <div class="page"> <section id="main"> <div id="chatLog"></div> </section> <header style="position: fixed; left: 10px; top: 0px;"> <hgroup> <h1>Chattin' it up</h1> </hgroup> </header> <footer style="position: fixed; left: 10px; bottom: 0;"> <a class="botToggle" href="#" onclick="toggleBot()">Toggle Bot On/Off</a> <textarea class="chatText" placeholder="Type a Message" autofocus></textarea><br /> <input id="submitBtn" type="submit" value="Send" /> </footer> <div id="typingIndicator" style="display: none;">Typing a Message</div> </div> </body> </html>
/** * HTML5 ✰ Boilerplate * * style.css contains a reset, font normalization and some base styles. * * Credit is left where credit is due. * Much inspiration was taken from these projects: * - yui.yahooapis.com/2.8.1/build/base/base.css * - camendesign.com/design/ * - praegnanz.de/weblog/htmlcssjs-kickstart */ /** * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark * html5doctor.com/html-5-reset-stylesheet/ */ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } ins { background-color: #ff9; color: #000; text-decoration: none; } mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } del { text-decoration: line-through; } abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } table { border-collapse: collapse; border-spacing: 0; } hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } input, select { vertical-align: middle; } /** * Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/ */ body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity. */ select, input, textarea, button { font:99% sans-serif; } /* Normalize monospace sizing: en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */ pre, code, kbd, samp { font-family: monospace, sans-serif; } /** * Minimal base styles. */ body, select, input, textarea { /* #444 looks better than black: twitter.com/H_FJ/statuses/11800719859 */ color: #444; /* Set your base font here, to apply evenly. */ font-family: Arial, Helvetica, sans-serif; } /* Headers (h1, h2, etc) have no default font-size or margin. Define those yourself. */ h1, h2, h3, h4, h5, h6 { font-weight: bold; } /* Always force a scrollbar in non-IE: html { overflow-y: scroll; } */ /* Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test */ a:hover, a:active { outline: none; } a, a:active, a:visited { color: #607890; } a:hover { color: #036; } ul, ol { margin-left: 2em; } ol { list-style-type: decimal; } /* Remove margins for navigation lists. */ nav ul, nav li { margin: 0; list-style:none; list-style-image: none; } small { font-size: 85%; } strong, th { font-weight: bold; } td { vertical-align: top; } /* Set sub, sup without affecting line-height: gist.github.com/413930 */ sub, sup { font-size: 75%; line-height: 0; position: relative; } sup { top: -0.5em; } sub { bottom: -0.25em; } pre { /* www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ */ white-space: pre; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; padding: 15px; } textarea { overflow: auto; } /* www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars/ */ .ie6 legend, .ie7 legend { margin-left: -7px; } /* thnx ivannikolic! */ /* Align checkboxes, radios, text inputs with their label by: Thierry Koblentz tjkdesign.com/ez-css/css/base.css */ input[type="radio"] { vertical-align: text-bottom; } input[type="checkbox"] { vertical-align: bottom; } .ie7 input[type="checkbox"] { vertical-align: baseline; } .ie6 input { vertical-align: text-bottom; } /* Hand cursor on clickable input elements. */ label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; } /* Webkit browsers add a 2px margin outside the chrome of form elements. */ button, input, select, textarea { margin: 0; } /* Colors for form validity. */ input:valid, textarea:valid { } input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; } .no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; } /* These selection declarations have to be separate. No text-shadow: twitter.com/miketaylr/status/12228805301 Also: hot pink. */ ::-moz-selection{ background: #F2D64C; color:#fff; text-shadow: none; } ::selection { background:#F2D64C; color:#fff; text-shadow: none; } /* j.mp/webkit-tap-highlight-color */ a:link { -webkit-tap-highlight-color: #F2D64C; } /* Make buttons play nice in IE: www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ */ button { width: auto; overflow: visible; } /* Bicubic resizing for non-native sized IMG: code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ */ .ie7 img { -ms-interpolation-mode: bicubic; } /** * Primary styles. * * Author: Jacob Gable */ .page { margin: 10px; } header h1 { font-size: 1.5em; } dt { display: none; height:1px;} dd { display: none; background-color: #3399FF; border-radius: 7px; -moz-border-radius: 7px; -webkit-border-radius: 7px; margin: 0px 20px 20px 0px; padding: 5px 10px; color: White; position: relative; } dd p { margin-bottom: 5px; } dd span { color: Black; font-weight: bold; } dd.reply { margin: 0 0 20px 20px; } dd .arrow { border-color: #3399FF transparent transparent transparent; border-style: solid; border-width: 10px 10px 10px 20px; width:0; height:0; position: absolute; bottom: -20px; right: 20px; } dd .arrowOver { border-color: White transparent transparent transparent; border-width: 10px 22px 10px 10px; bottom: -20px; right: 6px; } dd.reply { margin: 0 0 20px 20px; } dd.reply .arrow { border-color: #3399FF transparent transparent transparent; border-style: solid; border-width: 10px 20px 10px 10px; width:0; height:0; position: absolute; bottom: -20px; left: 20px ! important; } dd.reply .arrowOver { border-color: White transparent transparent transparent; border-width: 10px 10px 10px 22px; bottom: -20px; left: 6px ! important; } #typingIndicator { background-color: white; /* background-image: url('loady.gif'); */ background-position: left center; background-repeat: no-repeat; width: 100px; height: 20px; color: #686868; font-size: .8em; padding: 5px 0 0 25px; position: fixed; right: 15px; top: 10px; border-radius: 7px; -moz-border-radius: 7px; -webkit-border-radius: 7px; } #chatLog { padding: 10px; margin: 30px 0 90px 0; } .chatText { width: 100%; background-color: #BDBEBD; border: 0px Solid Black; line-height: 1.2em; font-size: 1.2em; height:auto; height: 3em; white-space: pre; } .placeholder{color:#8A8A8A}/* Placeholder text */ header { width: 100%; background-color: white; height: 35px; } header hgroup { margin-top: 10px; } footer { background-color: white; } footer input[type=submit] { background-color: #EDEDED; border: 1px Solid #AAAAAA; width: 100%; margin: 10px 0; min-height: 2em; } footer a.botToggle { float: right; margin-bottom: 5px; } /** * Non-semantic helper classes: please define your styles before this section. */ /* For image replacement. */ .ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; } /* Hide for both screenreaders and browsers: css-discuss.incutio.com/wiki/Screenreader_Visibility */ .hidden { display: none; visibility: hidden; } /* Hide only visually, but have it available for screenreaders: by Jon Neal. www.webaim.org/techniques/css/invisiblecontent/ & j.mp/visuallyhidden */ .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } /* Hide visually and from screenreaders, but maintain layout. */ .invisible { visibility: hidden; } /* The Magnificent Clearfix: Updated to prevent margin-collapsing on child elements. j.mp/bestclearfix */ .clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; visibility: hidden; } .clearfix:after { clear: both; } /* Fix clearfix: blueprintcss.lighthouseapp.com/projects/15318/tickets/5-extra-margin-padding-bottom-of-page */ .clearfix { zoom: 1; }