Commit: 8ee172a3c8d6290b53c7c15602681d69fc3ddad5

Author: Jon Adams | Date: 2010-01-24 22:33:46 -0800
Major polishing both visually and via js. Markdown help, cookies implemented, links open in new windows, pretty time format, doctitle alerts on new and 'direct' messages
diff --git a/config/bootstrap.php b/config/bootstrap.php index f2c6eff..6a89fc8 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -10,6 +10,7 @@ namespace lithium; use \lithium\core\Environment; use \lithium\core\Libraries; +use \lithium\storage\Session; /** * This is the path to the class libraries used by your application, and must contain a copy of the @@ -38,6 +39,12 @@ if (!include LITHIUM_LIBRARY_PATH . '/lithium/core/Libraries.php') { } /** + * Add the Lithium core library. This sets default paths and initializes the autoloader. You + * generally should not need to override any settings. + */ +Libraries::add('lithium'); + +/** * Optimize default request cycle by loading common classes. If you're implementing custom * request/response or dispatch classes, you can safely remove these. Actually, you can safely * remove them anyway, they're just there to give slightly you better out-of-the-box performance. @@ -62,11 +69,7 @@ require LITHIUM_LIBRARY_PATH . '/lithium/action/Response.php'; require LITHIUM_LIBRARY_PATH . '/lithium/template/View.php'; require LITHIUM_LIBRARY_PATH . '/lithium/template/view/Renderer.php'; -/** - * Add the Lithium core library. This sets default paths and initializes the autoloader. You - * generally should not need to override any settings. - */ -Libraries::add('lithium'); + /** * Add the application. You can pass a `'path'` key here if this bootstrap file is outside of @@ -74,117 +77,6 @@ Libraries::add('lithium'); */ Libraries::add('app'); -/** - * Add some plugins - */ -// Libraries::add('plugin', 'li3_docs'); - -/** - * This configures your session storage. The Cookie storage adapter must be connected first, since - * it intercepts any writes where the `'expires'` key is set in the options array. When creating a - * new application, it is suggested that you change the value of `'key'` below. - */ - -/** -* Session configuration -*/ -// use \lithium\storage\Session; -// -// Session::config(array( -// 'cookie' => array( -// 'adapter' => 'Cookie', -// 'name' => 'AppCookieName', -// 'expires' => '+5 days', -// 'domain' => '', -// 'path' => '/', -// 'filters' => array( -// // 'Encryption' => array('key' => '0409448a5206980ab15682c3281c1a3b1fb10c55') -// ) -// ), -// 'default' => array('adapter' => 'Php', 'filters' => array()) -// )); - -/** - * To enable admin or plugin routing, uncomment the following lines, and see `app/config/routes.php` - * to enable the admin routing namespace. - */ -// use \lithium\action\Dispatcher; -// -// Dispatcher::config(array('rules' => array( -// 'admin' => array('action' => 'admin_{:action}'), -// 'plugin' => array('controller' => '{:plugin}.{:controller}') -// ))); - -/* - * Inflector configuration example. If your application has custom singular or plural rules, or - * extra non-ASCII characters to transliterate, you can configure that by uncommenting the lines - * below. - */ -// use lithium\util\Inflector; -// -// Inflector::rules("plural", array( -// '/(s)tatus$/i' => '\1\2tatuses', -// '/^(ox)$/i' => '\1\2en', -// '/([m|l])ouse$/i' => '\1ice' -// )); -// -// Inflector::rules("uninflectedPlural", array('.*[nrlm]ese', '.*deer', '.*ois', '.*pox')); -// -// Inflector::rules("irregularPlural", array('atlas' => 'atlases', 'brother' => 'brothers')); -// -// Inflector::rules("singular", array( -// '/(s)tatuses$/i' => '\1\2tatus', -// '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i' -// )); - -/** - * Globalization (g11n) catalog configuration. The catalog allows for obtaining and - * writing globalized data. Each configuration can be adjusted through the following settings: - * - * - `'adapter' The name of a supported adapter. The builtin adapters are _memory_ (a - * simple adapter good for runtime data and testing), _gettext_, _cldr_ (for - * interfacing with Unicode's common locale data repository) and _code_ (used mainly for - * extracting message templates from source code). - * - * - `'path'` All adapters with the exception of the _memory_ adapter require a directory - * which holds the data. - * - * - `'scope'` If you plan on using scoping i.e. for accessing plugin data separately you - * need to specify a scope for each configuration, except for those using the _memory_ or - * _gettext_ adapter which handle this internally. - */ -// use lithium\g11n\Catalog; -// -// Catalog::config(array( -// 'runtime' => array('adapter' => 'Memory'), -// 'app' => array('adapter' => 'Gettext', 'path' => LITHIUM_APP_PATH . '/resources/po'), -// 'lithium' => array('adapter' => 'Gettext', 'path' => LITHIUM_LIBRARY_PATH . '/lithium/resources/po') -// )); - -/** - * Globalization runtime data. You can add globalized data during runtime utilizing a - * configuration set up to use the _memory_ adapter. - */ -// $data = array('en' => function($n) { return $n != 1 ? 1 : 0; }); -// Catalog::write('message.plural', $data, array('name' => 'runtime')); - -/** - * Enabling globalization integration. Classes in the framework are designed with - * globalization in mind. To enable globalization for these classes we just need to pass - * the needed data into them. - */ -// use lithium\util\Validator; -// use lithium\util\Inflector; -// -// Validator::add('postalCode', -// Catalog::read('validation.postalCode', array('en_US')) -// ); -// Inflector::rules('transliterations', -// Catalog::read('inflection.transliteration', array('en')) -// ); - -/** - * Your custom code goes here. - */ +Session::config(array('default' => array('adapter' => 'Cookie'))); ?> diff --git a/controllers/AnologueController.php b/controllers/AnologueController.php index 6f187a7..1bf93fe 100644 --- a/controllers/AnologueController.php +++ b/controllers/AnologueController.php @@ -4,7 +4,6 @@ namespace app\controllers; use \app\models\Anologue; use \lithium\storage\Session; -use \lithium\storage\session\adapter\Cookie; /** * The core controller for Anologue. @@ -14,16 +13,6 @@ use \lithium\storage\session\adapter\Cookie; class AnologueController extends \lithium\action\Controller { /** - * Constructor - */ - public function __construct($config = null) { - Session::config(array( - 'default' => array('adapter' => new Cookie()) - )); - parent::__construct($config); - } - - /** * This action is used to render the index view, which is essentially a static page. */ public function index() { @@ -107,7 +96,6 @@ class AnologueController extends \lithium\action\Controller { */ private function _manageCookie($data = array()) { $cookieKeys = array('author','email','scrolling','sounds', 'cookies'); - if ($data['cookies'] == 'true') { $user = array(); @@ -122,7 +110,7 @@ class AnologueController extends \lithium\action\Controller { Session::write('user', serialize($user)); } else { - Session::delete('user'); + Session::write('user', null); } } diff --git a/views/anologue/index.html.php b/views/anologue/index.html.php index 9333beb..1a3923d 100644 --- a/views/anologue/index.html.php +++ b/views/anologue/index.html.php @@ -1,4 +1,4 @@ -<div id="anologue-new"><?php echo $this->html->link('start an anologue &raquo;', array('controller' => 'anologue', 'action' => 'add'), array('escape' => false)); ?></div> +<div id="anologue-new"><?php echo $this->html->link('new &nbsp;anologue &raquo;', array('controller' => 'anologue', 'action' => 'add'), array('escape' => false)); ?></div> <div class="header"> <header> @@ -20,7 +20,7 @@ <p><strong>google wave</strong> may be an option... someday. even so, in my experience with the beta so far i'd have to argue that multi-threaded chats are actually less productive.</p> <p>so, i needed something different.</p> - <h2 class="sub">a slightly different approach to an old problem</h2> + <h2 class="sub">a &nbsp;slightly &nbsp;different &nbsp;approach</h2> <div class="aside excellence"> <aside> @@ -35,7 +35,7 @@ <p><strong>no accounts. no installations. no way?! <em>yes, way!</em></strong></p> <p><?php echo $this->html->link('your "chat room" is created by the time this link loads', array('controller' => 'anologue', 'action' => 'add')); ?>. invite whoever you want by giving them your unique link, and chat away.</p> - <h2 class="sub">let's make this better, together</h2> + <h2 class="sub">let's &nbsp;make &nbsp;this &nbsp;better, &nbsp;together</h2> <p>perhaps best of all: <?php echo $this->html->link('this is open source', 'http://rad-dev.org/lithium_anologue'); ?>. built with <?php echo $this->html->link('php 5.3.1', 'http://php.net'); ?>, using the most non-heinous, totally rad <?php echo $this->html->link('lithium framework', 'http://li3.rad-dev.org'); ?>, <?php echo $this->html->link('couchdb', 'http://couchdb.apache.org'); ?>, <?php echo $this->html->link('jquery', 'http://jquery.com'); ?>, a few other scripts as well as some classy, original and <?php echo $this->html->link('established', 'http://www.pinvoke.com/'); ?> iconography for ui; all coming together for the conversational goodness you're about to experience.</p> <p class="last">contribute to the core or download the source and setup your own. this one's for you, internets.</p> diff --git a/views/anologue/view.html.php b/views/anologue/view.html.php index 4ff3cdf..35b48f9 100644 --- a/views/anologue/view.html.php +++ b/views/anologue/view.html.php @@ -20,7 +20,7 @@ <?php echo $this->html->link( $data->id, - array('action' => 'view', 'id' => $data->_id), + array('controller' => 'anologue', 'action' => 'view', 'id' => $data->id), array('title' => 'Copy this url and give it to others') ); ?> @@ -41,13 +41,13 @@ <?php foreach ($data->messages as $key => $message) { ?> <li class="message" id="message-<?php echo md5($message->timestamp . $message->author);?>"> <ul class="data"> - <li class="time"><?php echo date('G:i:s', $message->timestamp);?></li> + <li class="time"><span class="timestamp"><?php echo $message->timestamp;?></span><span class="human-time"></span></li> <li class="ip"><?php echo $message->ip;?></li> <li class="author"> <?php echo $this->html->image( "http://gravatar.com/avatar/{$message->email}?s=16&d={$avatar}"); ?> <span title="<?php echo $this->html->escape($message->author);?>"> - &laquo; <?php echo $this->html->escape($message->author);?> &raquo; + <?php echo $this->html->escape($message->author);?> </span> </li> <li class="text"> @@ -67,11 +67,11 @@ <div class="anologue-settings"> <div class="input name"> <label class="icon" for="anologue-author" title="Your name"><span>Your name</span></label> - <input type="text" name="anologue-author" id="anologue-author" value="<?php echo ($user['author']) ?: ''; ?>" /> + <input type="text" name="anologue-author" id="anologue-author" value="<?php echo ($user['author']) ?: ''; ?>" tabindex="1" /> </div> <div class="input email"> <label class="icon" for="anologue-email" title="Your e-mail address"><span>Your e-mail</span></label> - <input type="text" name="anologue-email" id="anologue-email" value="<?php echo ($user['email']) ?: ''; ?>" /> + <input type="text" name="anologue-email" id="anologue-email" value="<?php echo ($user['email']) ?: ''; ?>" tabindex="2" /> </div> <div class="checkbox first sound"> <label class="icon <?php echo ($user['sounds'] == 'false') ? 'disabled' : ''; ?>" title="Toggle sounds"><span>Toggle sounds</span></label> @@ -96,7 +96,8 @@ <div class="anologue-speak"> <div class="input textarea"> <label class="icon" for="anologue-text" title="Type what you want to say and press enter"><span>you say:</span></label> - <textarea name="anologue-text" id="anologue-text"></textarea> + <textarea name="anologue-text" id="anologue-text" tabindex="3"></textarea> + <label id="markdown-help">markdown &nbsp; syntax</label> </div> <div class="submit"> <button id="anologue-submit"><span>send</span></button> @@ -111,8 +112,8 @@ <audio id="anologue-speaker"></audio> <?php echo $this->html->script(array( - 'http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js', - 'md5.jquery.js', 'showdown.js', 'anologue.js', + 'http://code.jquery.com/jquery-1.4.min.js', + 'md5.jquery.js', 'showdown.js', 'pretty.js', 'anologue.js', )); ?> <script type="text/javascript" charset="utf-8"> $(document).ready(function() { @@ -120,7 +121,7 @@ id: '<?=$data->id?>', base: '<?php echo $this->_request->env('base') ?>', line: <?php echo count($data->messages); ?>, - icon: '<?php echo $avatar; ?>' + icon: '<?php echo $avatar; ?>' }); }); </script> diff --git a/webroot/css/anologue.css b/webroot/css/anologue.css index acf8352..b82b681 100644 --- a/webroot/css/anologue.css +++ b/webroot/css/anologue.css @@ -1,21 +1,28 @@ +@font-face { font-family: "Anologue Standard"; src: url(../media/font/PT_Sans.ttf); } +@font-face { font-family: "Anologue Bold"; src: url(../media/font/PT_Sans_Bold.ttf); } +@font-face { font-family: "Anologue Titling"; src: url(../media/font/Bebas.ttf); } + * { margin:0; padding:0; } -h1.title { font-size:4em; opacity:.5; } - h1 a { color:black; opacity:.05; border:none; } - h1 a:hover { color:black; opacity:.15; } -h2.sub { font-size:3em; font-weight:normal; opacity:.5; margin:.5em 0 0 0; } -h3.hash { opacity:.5; } - h3.hash a { border:none; } - h3.hash a:hover { } +h1, h2, h3, #markdown-help { font-family:"Anologue Titling", Helvetica, Arial, sans-serif; font-weight:normal; text-transform:uppercase; } +strong { font-family:"Anologue Bold", Helvetica, Arial, sans-serif; font-weight:normal; } + +h1.title { font-size:9em; text-shadow: 1px 1px 4px rgba(0,0,0,.75); color:white; text-transform:uppercase; } + h1 a { color:black; border:none; opacity:.05; } + h1 a:hover { color:black; } +h2.sub { font-size:2.75em; font-weight:normal !important; opacity:.5; margin:.75em 0 .25em 0; text-shadow: 2px 2px 4px rgba(0,0,0,.25); } +h3.hash { } + h3.hash a { border:none; color:#77458c; text-shadow: 1px 1px 1px rgba(0,0,0,.15); } + h3.hash a:hover { } a { color:#4c2c59; text-decoration:none; border-bottom:1px dotted #77458c; } a:hover { color:#77458c; } -html, body, input, password, textarea { background:white; color:black; font-family:Helvetica, Arial, sans-serif; } +html, body, input, password, textarea { background:white; color:black; font-family: "Anologue Standard", Helvetica, Arial, sans-serif; } body { padding:1em; } body.index { padding:0; } -p { padding:.5em 0; } +p { padding:.5em 0; line-height:140%; } p.last { padding:.5em 0 4em 0; } .width-constraint { display:block; width:1002px; margin:0 auto; } @@ -25,50 +32,64 @@ p.last { padding:.5em 0 4em 0; } .footer { text-align:center; padding:0 0 2em 0; } .footer a { border:none; margin:0 auto; } -#anologue-new a { display:block; font-weight:bold; padding:1em; font-size:2em; float:right; background:black; background:rgba(0,0,0,.35); color:white; border:none; } -#anologue-logo { padding:0 2.75em; display:block; } +#anologue-new a { display:block; padding:1em; font-size:2em; float:right; background:black; background:rgba(0,0,0,.35); color:white; border:none; -moz-box-shadow: inset 1px 1px 36px rgba(0,0,0,.5); -webkit-box-shadow: inset 1px 1px 36px rgba(0,0,0,.5); box-shadow: inset 1px 1px 36px rgba(0,0,0,.5); text-shadow: 1px 1px 4px rgba(0,0,0,.75); font-family:"Anologue Titling", Helvetica, Arial, sans-serif; } +#anologue-logo { padding:0; display:block; } .excellence { float:right; padding:1em; background:#fafafa; margin:0 0 1em 1em; } .excellence img { margin:0 0 .5em 0; border:1em solid #f0f0f0; } .excellence span { display:block; font-style:italic; font-size:.65em; text-align:center; } .anologue-titling { float:left; } - #anologue-help { position:fixed; z-index:50; bottom:0; left:0; display:block; width:100%; background:black; background:rgba(0,0,0,.95); color:white; text-align:left; font-size:.85em; } + #anologue-help { position:fixed; z-index:50; bottom:-800px; left:0; display:block; width:100%; background:black; background:rgba(0,0,0,.95); color:white; text-align:left; font-size:.85em; } #anologue-help .padding { padding: 2em 2em 13em 2em; } - #anologue-help h2 { font-size:3em; } - #anologue-help p { opacity:.75; } + #anologue-help h2 { font-size:9em; margin:0; padding:0; } + #anologue-help .markdown-help h2 { font-size:6em; } + #anologue-help h2 span { display:inline; } + #anologue-help p { } + #anologue-help .markdown-help p { } #anologue-help a { color:#77458c; } #anologue-close-help { position:absolute; top:.25em; right:.5em; } button.close { border:none; background:none; display:block; width:28px; height:28px; } button.close span { display:block; width:28px; height:0; padding:28px 0 0 0; overflow:hidden; background:url(../img/icons/cross.png) no-repeat center center; } .anologue { position:relative; z-index:10; width:100%; display:block; clear:both; margin:0 0 8.75em 0; padding:2em 0 0; } + +/** Anologue Messages **/ + .anologue .message { position:relative; display:block; background:#fafafa; border-bottom:3px solid white; vertical-align:top; font-size:.85em; min-height:3em; } .anologue .message:hover { background:white; } .anologue .data { position:relative; } .anologue .data > li { display:block; padding:.5em; position:absolute; left:0; top:0; } - .anologue li.time { position:absolute; z-index:50; opacity:0; white-space:nowrap; text-align:center; width:4em; top:0; left:0; } - .anologue li.ip { position:absolute; z-index:50; opacity:0; whitespace:nowrap; text-align:left; width:4em; left:.25em; top:1.6em; font-size:.85em; } - .anologue .message:hover li.time, .anologue .message:hover li.ip { opacity:.25; } - .anologue li.author { font-weight:bold; white-space:nowrap; text-align:right; width:14em; padding:.75em .5em .25em 5.5em; min-height:2em; background:#ededed; } + .anologue li.time { position:absolute; z-index:50; white-space:nowrap; text-align:center; width:4em; top:0; left:0; padding:.75em .5em .5em .75em; } + .anologue li.time .timestamp { display:none; } + .anologue li.time .human-time { opacity:.15; font-size:.85em; } + .anologue .message:hover .human-time { opacity:.25; } + .anologue li.ip { position:absolute; z-index:50; opacity:0; whitespace:nowrap; text-align:left; width:4em; left:.5em; top:2em; font-size:.75em; } + .anologue .message:hover li.ip { opacity:.15; } + .anologue li.author { white-space:nowrap; text-align:right; width:14em; padding:.75em .5em .25em 5.5em; min-height:2em; background:#ededed; } .anologue li.author img { float:right; margin:0 0 0 .5em; } - .anologue li.author span { max-width:12em; overflow:hidden; display:block; } + .anologue li.author span { max-width:12em; overflow:hidden; display:block; opacity:.5; } + .anologue .message:hover li.author span { opacity:1; } .anologue li.text { position:relative; padding:.25em .5em 0 2em; margin:0 0 0 19.90em; background:url(../img/pointer.png) no-repeat top left; min-height:2.75em; } .anologue .text a { border-bottom:1px dotted #77458c; } .anologue .text .markdown { } .anologue .text .markdown ul { padding:.25em 1em; } - - #anologue-speech-bar { position:fixed; z-index:50; bottom:0; left:0; display:block; width:100%; min-width:1006px; clear:both; background:black; background-color:rgba(0,0,0,.85); color:white; } + +/** Anologue Footer "Speech" Bar **/ + + #anologue-speech-bar { position:fixed; z-index:50; bottom:-200px; left:0; display:block; width:100%; min-width:1006px; clear:both; background:black; background-color:rgba(0,0,0,.85); color:white; } .purple-background { background:#4c2c59 url(../img/bg.png); background-color:rgba(76,44,89,.75); height:150px; } .twenty-percent { float:left; min-width:275px; } .eighty-percent { float:left; width:70%; min-width:725px; } - + +/** Anologue Settings **/ + .anologue-settings, .anologue-speak { padding:1em; } .anologue-settings > div { padding:0 0 2px 0; } .anologue-settings .input, .anologue-settings .about { clear:both; } .anologue-settings input[type=text] { width:14em; border:none; padding:4px 6px; background:#4c2c59; color:white; height:16px; font-size:14px; } - .anologue-settings input[type=text]:focus { outline:none; background:white; color:black; } + .anologue-settings input[type=text]:focus { outline:none; background:white; color:black; -moz-box-shadow: inset 1px 1px 3px rgba(0,0,0,.75); -webkit-box-shadow: inset 1px 1px 3px rgba(0,0,0,.75); box-shadow: inset 1px 1px 3px rgba(0,0,0,.75); } .anologue-settings .checkbox { float:left; padding:10px 0 0 0; margin: 0 2px 0 0; } .anologue-settings .checkbox.first { clear:both; } @@ -76,11 +97,14 @@ p.last { padding:.5em 0 4em 0; } .anologue-settings .about a:hover { color:white; } /** Label Icons **/ + label.icon { display:block; width:28px; height:28px; } .anologue-settings label.icon { float:left; } label.icon span { display:block; width:28px; height:0; padding:28px 0 0 0; overflow:hidden; } .icon.disabled span { background:url(../img/icons/slash.png) no-repeat center center; } + .checkbox label.icon:hover { background-color:white; -moz-box-shadow: inset 1px 1px 4px rgba(0,0,0,.75); -webkit-box-shadow: inset 1px 1px 4px rgba(0,0,0,.75); box-shadow: inset 1px 1px 4px rgba(0,0,0,.75); } + .name .icon { background:url(../img/icons/name-tag.png) no-repeat center center; } .email .icon { background:url(../img/icons/mail.png) no-repeat center center; } @@ -91,7 +115,10 @@ p.last { padding:.5em 0 4em 0; } .anologue-speak .icon { background:url(../img/anonymous.png) no-repeat center center; } /** Speech Input **/ + .anologue-speak label { float:left; } .anologue-speak textarea { padding:4px 6px; width:90%; height:6em; background:#4c2c59; color:white; border:none; font-size:14px; } - .anologue-speak textarea:focus { outline:none; background:white; color:black; } + .anologue-speak textarea:focus { outline:none; background:white; color:black; -moz-box-shadow: inset 1px 1px 6px rgba(0,0,0,.75); -webkit-box-shadow: inset 1px 1px 6px rgba(0,0,0,.75); box-shadow: inset 1px 1px 6px rgba(0,0,0,.75); } + #markdown-help { font-size:.75em; border:none; margin:.25em 0 .25em 28px; color:white; opacity:.5; } + #markdown-help:hover { opacity:1; } .anologue-speak .submit { display:none; } diff --git a/webroot/js/anologue.js b/webroot/js/anologue.js index 6d4028e..45532b3 100644 --- a/webroot/js/anologue.js +++ b/webroot/js/anologue.js @@ -22,13 +22,31 @@ var anologue = { anologue.closeHelp(); return false; }); + $("#markdown-help").click(function() { + anologue.markdownHelp(); + return false; + }); + this.setupSubmit(); this.markdown(); - $("#anologue-help").css("bottom", '-400px').animate({bottom: 0}, 2000); - $("#anologue-speech-bar").css("bottom", '-200px').animate({bottom: 0}, 2000); - $("#anologue-author").focus(); + this.fireworks(); this.setupSpeaker(); this.listener(); + this.humanizeTimes(); + this.humanizeTimesTimer(); + + $('body').focusin(function() { + anologue.resetTitle(); + }); + $('body').click(function() { + anologue.resetTitle(); + }); + }, + + fireworks: function() { + $("#anologue-help").animate({bottom: 0}, 2500); + $("#anologue-speech-bar").animate({bottom: 0}, 1500); + $("#anologue-author").focus(); }, setupSubmit: function() { @@ -63,6 +81,7 @@ var anologue = { run: function () { clearTimeout(this.timeout); this.timeout = setTimeout(function() { + $("a").attr({"target":"_blank"}); anologue.listener(); }, 2000); }, @@ -119,22 +138,31 @@ var anologue = { //output messages render: function(message) { - var timeroo = new Date(); - var timestamp = timeroo.getHours() + ':' + timeroo.getMinutes() + ':' + timeroo.getSeconds(); var id = 'message-' + $.md5(message.timestamp + message.author); - var html = '<li class="message" id="' + id + '" style="display:none;"><ul class="data"><li class="time">' + timestamp + '</li><li class="ip">' + message.ip + '</li><li class="author"><img class="gravatar" src="http://gravatar.com/avatar/' + message.email + '?s=16&d=' + this._config.icon + '" border="0" /> <span title="' + $('<div/>').text(message.author).html() + '">&laquo; ' + $('<div/>').text(message.author).html() + ' &raquo;</span> </li><li class="text"><div class="markdown"><pre>' + $('<div/>').text(message.text).html() + '</pre></div></li></ul></li>'; + var html = '<li class="message" id="' + id + '" style="display:none;"><ul class="data"><li class="time"><span class="timestamp">' + message.timestamp + '</span><span class="human-time">' + this.humanizeTime(message.timestamp) + '</span></li><li class="ip">' + message.ip + '</li><li class="author"><img class="gravatar" src="http://gravatar.com/avatar/' + message.email + '?s=16&d=' + this._config.icon + '" border="0" /> <span title="' + $('<div/>').text(message.author).html() + '">' + $('<div/>').text(message.author).html() + '</span> </li><li class="text"><div class="markdown"><pre>' + $('<div/>').text(message.text).html() + '</pre></div></li></ul></li>'; $("#anologue").append(html); + + var docTitle = message.author+' posted a new message'; + var soundDisabled = $('.anologue-settings .sound .icon').hasClass('disabled'); + + var user = $('#anologue-author').val(); + if (!soundDisabled) { // lazy check to not trigger sound on your message - var user = $('#anologue-author').val(); if (message.author != user && user != '') { var userRegex = new RegExp(user.toLowerCase(), 'i'); if (userRegex.test(message.text)) { this.hey(); + var docTitle = message.author+' mentioned you in a new message'; } } } + + if (message.author != user) { + this.updateTitle(docTitle); + } + $('#'+id).animate({ opacity: 'show' }, 1000); @@ -194,7 +222,7 @@ var anologue = { closeHelp: function() { if (!$("#anologue-help").hasClass('closed')) { - $("#anologue-help").animate({bottom: '-400px'}, 1000); + $("#anologue-help").animate({bottom: '-500px'}, 1000); $("#anologue-help").addClass('closed'); } return false; @@ -203,6 +231,60 @@ var anologue = { getOption: function(parentClass) { var disabled = $('.anologue-settings '+parentClass+' .icon').hasClass('disabled'); return !disabled; + }, + + humanizeTimesTimeout: null, + + humanizeTimesTimer: function() { + clearTimeout(this.humanizeTimesTimeout); + anologue.humanizeTimes(); + this.humanizeTimesTimeout = setTimeout(function() { + anologue.humanizeTimesTimer(); + }, 15000); + }, + + humanizeTimes: function() { + $('li.time').each(function() { + var time = $(this).children('.timestamp').first().text(); + var prettyTime = anologue.humanizeTime(time); + $(this).children('.human-time').first().text(prettyTime); + }); + }, + + humanizeTime: function(timestamp) { + return PrettyDate.convert(timestamp); + }, + + resetTitle: function() { + document.title = 'anologue'; + }, + + updateTitle: function(msg) { + document.title = msg + ' - anologue'; + }, + + markdownHelp: function() { + if (!$('#anologue-help .padding').hasClass("markdown-help")) { + var html = '<h2>Markdown &nbsp;Syntax</h2><p># header 1 &nbsp; &nbsp; ## header 2 &nbsp; &nbsp; <em>*italic*</em> &nbsp; &nbsp; <strong>**bold**</strong> &nbsp; &nbsp; - unordered list &nbsp; &nbsp; 1. ordered list &nbsp; &nbsp; [a link](http://example.com/) &nbsp; &nbsp; ![image alt text](http://example.com/image.jpg)</p>'; + if (!$("#anologue-help").hasClass("closed")) { + $("#anologue-help").animate({bottom: '-500px'}, 1000, function() { + $("#anologue-help").addClass('closed'); + $("#anologue-help .padding").html(html).addClass("markdown-help"); + anologue.showMarkdownHelp(); + }); + } else { + $("#anologue-help .padding").html(html).addClass("markdown-help"); + this.showMarkdownHelp(); + } + } else { + this.showMarkdownHelp(); + } + }, + + showMarkdownHelp: function() { + $("#anologue-help").animate({bottom: 0}, 1000, function() { + $("#anologue-help").removeClass('closed'); + }); } } diff --git a/webroot/js/pretty.js b/webroot/js/pretty.js new file mode 100644 index 0000000..c33b38f --- /dev/null +++ b/webroot/js/pretty.js @@ -0,0 +1,47 @@ +/** + * Found the core of this script on John Resig's blog in the comments, by Dean Landoldt. Made a + * number of tweaks to work for what I need, and encapsulated the whole thing. Muchas gracias. + * + * @see http://deanlandolt.com/ + * @see http://ejohn.org/ + */ + +var PrettyDate = { + formats: [ + [60, 'just now', "now"], // 60 + [120, '1 minute ago', '1 minute from now'], // 60*2 + [3600, 'minutes', 60], // 60*60, 60 + [7200, '1 hour ago', '1 hour from now'], // 60*60*2 + [86400, 'hours', 3600], // 60*60*24, 60*60 + [172800, 'yesterday', 'tomorrow'], // 60*60*24*2 + [604800, 'days', 86400], // 60*60*24*7, 60*60*24 + [1209600, 'last week', 'next week'], // 60*60*24*7*4*2 + [2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7 + [4838400, 'last month', 'next month'], // 60*60*24*7*4*2 + [29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4 + [58060800, 'last year', 'next year'], // 60*60*24*7*4*12*2 + [2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12 + [5806080000, 'last century', 'next century'], // 60*60*24*7*4*12*100*2 + [58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100 + ], + + convert: function(timestamp){ + var now = new Date().getTime(); + var then = new Date().setTime(timestamp * 1000); + var seconds = (now - then) / 1000; + var token = 'ago', list_choice = 1; + if (seconds < 0) { + seconds = Math.abs(seconds); + token = 'from now'; + list_choice = 2; + } + var i = 0, format; + while (format = this.formats[i++]) if (seconds < format[0]) { + if (typeof format[2] == 'string') + return format[list_choice]; + else + return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token; + } + return 'Y2K'; + } +} diff --git a/webroot/media/font/Bebas.ttf b/webroot/media/font/Bebas.ttf new file mode 100644 index 0000000..eaff486 Binary files /dev/null and b/webroot/media/font/Bebas.ttf differ diff --git a/webroot/media/font/PT_Sans.ttf b/webroot/media/font/PT_Sans.ttf new file mode 100644 index 0000000..2a84833 Binary files /dev/null and b/webroot/media/font/PT_Sans.ttf differ diff --git a/webroot/media/font/PT_Sans_Bold.ttf b/webroot/media/font/PT_Sans_Bold.ttf new file mode 100644 index 0000000..ccea2cc Binary files /dev/null and b/webroot/media/font/PT_Sans_Bold.ttf differ diff --git a/webroot/media/font/Paratype PT Sans Free Font License.txt b/webroot/media/font/Paratype PT Sans Free Font License.txt new file mode 100644 index 0000000..c908d2b --- /dev/null +++ b/webroot/media/font/Paratype PT Sans Free Font License.txt @@ -0,0 +1,26 @@ +Copyright © 2009 ParaType Ltd. +with Reserved Names "PT Sans" and "ParaType". + +FONT LICENSE + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining a copy of the font software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the font software, subject to the following conditions: + +1) Neither the font software nor any of its individual components, in original or modified versions, may be sold by itself. + +2) Original or modified versions of the font software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. + +3) No modified version of the font software may use the Reserved Name(s) or combinations of Reserved Names with other words unless explicit written permission is granted by the ParaType. This restriction only applies to the primary font name as presented to the users. + +4) The name of ParaType or the author(s) of the font software shall not be used to promote, endorse or advertise any modified version, except to acknowledge the contribution(s) of ParaType and the author(s) or with explicit written permission of ParaType. + +5) The font software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. + +TERMINATION & TERRITORY +This license has no limits on time and territory, but it becomes null and void if any of the above conditions are not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL PARATYPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +ParaType Ltd +http://www.paratype.ru \ No newline at end of file