Commit: a6f09b77f0897af70a72bd40355d38001ac36053

Author: Jon Adams | Date: 2010-03-09 02:38:51 -0800
0.7 Compability, Slight refactoring, oembed support, ui tweaks
diff --git a/config/bootstrap.php b/config/bootstrap.php index 6a89fc8..5af1f2a 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -2,23 +2,17 @@ /** * Lithium: the most rad php framework * - * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) * @license http://opensource.org/licenses/bsd-license.php The BSD License */ -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 * Lithium core. By default, this directory is named 'libraries', and resides in the same * directory as your application. If you use the same libraries in multiple applications, you can * set this to a shared path on your server. */ -define('LITHIUM_LIBRARY_PATH', dirname(dirname(__DIR__)) . '/lithium/libraries'); +define('LITHIUM_LIBRARY_PATH', dirname(dirname(__DIR__)) . '/libraries'); /** * This is the path to your application's directory. It contains all the sub-folders for your @@ -39,43 +33,33 @@ 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. + * This file contains the loading instructions for all class libraries used in the application, + * including the Lithium core, and the application itself. These instructions include library names, + * paths to files, and any applicable class-loading rules. Also includes any statically-loaded + * classes to improve bootstrap performance. */ -Libraries::add('lithium'); +require __DIR__ . '/bootstrap/libraries.php'; /** - * 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. + * Include this file if your application uses a database connection. */ -require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/util/Inflector.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/util/Set.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/util/String.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/http/Base.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/http/Media.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/http/Request.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/http/Response.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/http/Route.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/action/Controller.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/action/Dispatcher.php'; -require LITHIUM_LIBRARY_PATH . '/lithium/action/Request.php'; -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'; - +require __DIR__ . '/connections.php'; +/** + * This file defines bindings between classes which are triggered during the request cycle, and + * allow the framework to automatically configure its environmental settings. You can add your own + * behavior and modify the dispatch cycle to suit your needs. + */ +require __DIR__ . '/bootstrap/action.php'; /** - * Add the application. You can pass a `'path'` key here if this bootstrap file is outside of - * your main application, but generally you should not need to change any settings. + * This file contains configurations for connecting to external caching resources, as well as + * default caching rules for various systems within your application */ -Libraries::add('app'); +require __DIR__ . '/bootstrap/cache.php'; + + +use \lithium\storage\Session; Session::config(array('default' => array('adapter' => 'Cookie'))); diff --git a/config/bootstrap/action.php b/config/bootstrap/action.php new file mode 100644 index 0000000..6139e86 --- /dev/null +++ b/config/bootstrap/action.php @@ -0,0 +1,55 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +/** + * This file contains a series of method filters that allow you to intercept different parts of + * Lithium's dispatch cycle. The filters below are used for on-demand loading of routing + * configuration, and automatically configuring the correct environment in which the application + * runs. + * + * For more information on in the filters system, see `lithium\util\collection\Filters`. + * + * @see lithium\util\collection\Filters + */ + +use \lithium\core\Libraries; +use \lithium\net\http\Router; +use \lithium\core\Environment; +use \lithium\action\Dispatcher; + +/** + * This filter loads all application routes in all plugins, loading the default application routes + * last. Change this code if plugin routes must be loaded in a specific order, or if application + * routes must be loaded first (in which case the catch-all routes should be removed). If + * `Dispatcher::run()` is called multiple times in the course of a single request, change the + * `include`s to `include_once`. + * + * @see lithium\net\http\Router + */ +Dispatcher::applyFilter('run', function($self, $params, $chain) { + foreach (array_reverse(Libraries::get()) as $name => $config) { + if ($name === 'lithium') continue; + $file = "{$config['path']}/config/routes.php"; + file_exists($file) ? include $file : null; + } + return $chain->next($self, $params, $chain); +}); + +/** + * Intercepts the `Dispatcher` as it finds a controller object, and passes the `'request'` parameter + * to the `Environment` class to detect which environment the application is running in. + * + * @see lithium\action\Request + * @see lithium\core\Environment + */ +Dispatcher::applyFilter('_callable', function($self, $params, $chain) { + Environment::set($params['request']); + return $chain->next($self, $params, $chain); +}); + +?> \ No newline at end of file diff --git a/config/bootstrap/cache.php b/config/bootstrap/cache.php new file mode 100644 index 0000000..2039220 --- /dev/null +++ b/config/bootstrap/cache.php @@ -0,0 +1,44 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +/** + * This file creates a default cache configuration using the most optimized adapter available, and + * uses it to provide default caching for high-overhead operations. + */ +use lithium\storage\Cache; +use lithium\core\Libraries; +use lithium\action\Dispatcher; +use lithium\storage\cache\adapter\Apc; + +/** + * If APC is not available and the cache directory is not writeable, bail out. + */ +if (!$apcEnabled = Apc::enabled() && !is_writable(LITHIUM_APP_PATH . '/resources/tmp/cache')) { + return; +} + +Cache::config(array( + 'default' => array( + 'adapter' => '\lithium\storage\cache\adapter\\' . ($apcEnabled ? 'Apc' : 'File') + ) +)); + +Dispatcher::applyFilter('run', function($self, $params, $chain) { + if ($cache = Cache::read('default', 'core.libraries')) { + $cache = (array) unserialize($cache) + Libraries::cache(); + Libraries::cache($cache); + } + $result = $chain->next($self, $params, $chain); + + if ($cache != Libraries::cache()) { + Cache::write('default', 'core.libraries', serialize(Libraries::cache()), '+1 day'); + } + return $result; +}); + +?> \ No newline at end of file diff --git a/config/bootstrap/libraries.php b/config/bootstrap/libraries.php new file mode 100644 index 0000000..e103df1 --- /dev/null +++ b/config/bootstrap/libraries.php @@ -0,0 +1,53 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +use lithium\core\Libraries; + +/** + * 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. + */ +require LITHIUM_LIBRARY_PATH . '/lithium/core/Object.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/core/StaticObject.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/util/Collection.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/util/collection/Filters.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/util/Inflector.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/util/String.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/core/Adaptable.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/core/Environment.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Base.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Media.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Request.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Response.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Route.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/net/http/Router.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/action/Controller.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/action/Dispatcher.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/action/Request.php'; +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'; +require LITHIUM_LIBRARY_PATH . '/lithium/template/view/Compiler.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/template/view/adapter/File.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/storage/Cache.php'; +require LITHIUM_LIBRARY_PATH . '/lithium/storage/cache/adapter/Apc.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 + * your main application, but generally you should not need to change any settings. + */ +Libraries::add('app', array('default' => true)); + +?> \ No newline at end of file diff --git a/config/connections.php b/config/connections.php index fa5f99f..5ea54ea 100644 --- a/config/connections.php +++ b/config/connections.php @@ -8,10 +8,11 @@ use \lithium\data\Connections; -Connections::add('default', 'http', array( +Connections::add('default', array( + 'type' => 'Http', 'adapter' => 'CouchDb', - 'host' => '127.0.0.1', - 'port' => 5984, + 'database' => 'anologue', + 'host' => 'localhost' )); ?> diff --git a/config/routes.php b/config/routes.php index 6512c18..e2c4d4b 100644 --- a/config/routes.php +++ b/config/routes.php @@ -1,6 +1,6 @@ <?php -use \lithium\http\Router; +use \lithium\net\http\Router; /** * Connect the testing routes. diff --git a/config/switchboard.php b/config/switchboard.php deleted file mode 100644 index bd5adcf..0000000 --- a/config/switchboard.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Lithium: the most rad php framework - * - * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) - * @license http://opensource.org/licenses/bsd-license.php The BSD License - */ - -/** - * Welcome to the switchboard. This file contains a series of method filters that allow you to - * intercept different parts of Lithium's request cycle as they happen. You can apply filters to - * any object method that has a `@filter` flag in its API documentation. - * - * For more information on in the filters system, see `lithium\util\collection\Filters`. - * - * @see lithium\util\collection\Filters - */ - -use \lithium\http\Router; -use \lithium\core\Environment; -use \lithium\action\Dispatcher; - -/** - * Loads application routes before the request is dispatched. Change this to `include_once` if - * more than one request cycle is executed per HTTP request. - * - * @see lithium\http\Router - */ -Dispatcher::applyFilter('run', function($self, $params, $chain) { - include __DIR__ . '/routes.php'; - return $chain->next($self, $params, $chain); -}); - -/** - * Intercepts the `Dispatcher` as it finds a controller object, and passes the `'request'` parameter - * to the `Environment` class to detect which environment the application is running in. - * - * @see lithium\action\Request - * @see lithium\core\Environment - */ -Dispatcher::applyFilter('_callable', function($self, $params, $chain) { - Environment::set($params['request']); - return $chain->next($self, $params, $chain); -}); - -?> diff --git a/extensions/helper/Oembed.php b/extensions/helper/Oembed.php new file mode 100644 index 0000000..dbfc80f --- /dev/null +++ b/extensions/helper/Oembed.php @@ -0,0 +1,41 @@ +<?php + +namespace app\extensions\helper; + +class Oembed extends \lithium\template\Helper { + + /** + * Method to wrap urls within a string with an html anchor tag with a class for use with oEmbed. + * + * @param string $string string to search for urls + * @param array|string $options if string, assumed classname to use for anchors. otherwise, + * array used to merge against default options. + */ + public static function classify($string = null, $options = array()) { + $defaults = array( + 'class' => 'oembed', + 'markdown' => false + ); + + if (!empty($options) && is_string($options)) { + $options = array('class' => $options); + } + + extract($options + $defaults); + + if (!$options['markdown']) { + $link = "<a href=\"$1\" class=\"{$class}\">$1</a>"; + } else { + $link = "[$1]($1)"; + } + $string = preg_replace( + '@((?<![\[\(])https?://([-\w\.]+)+(:\d+)?(/([-\w/_\.]*(#?)([-\w]+)(\?\S+)?)?)?)@', + $link, + $string + ); + return $string; + } + +} + +?> \ No newline at end of file diff --git a/models/Anologue.php b/models/Anologue.php index e4f46d4..975cbdc 100644 --- a/models/Anologue.php +++ b/models/Anologue.php @@ -3,6 +3,7 @@ namespace app\models; use \lithium\data\model\Document; +use \app\extensions\helper\Oembed; /** * The core model and messages container for Anologue. @@ -28,6 +29,7 @@ class Anologue extends \lithium\data\Model { * * @var array * @see app\models\Anologue::addMessage() + * @todo move to Message model */ protected static $_defaultMessage = array( 'author' => 'anonymous', @@ -38,13 +40,13 @@ class Anologue extends \lithium\data\Model { ); /** - * Create a new analogue using schema. + * Create a new anologue using schema. * * @param array $data * @return void * @see lithium\data\Model::create() */ - public static function create($data = array()) { + public static function create(array $data = array()) { $default = array( 'messages' => null ); @@ -61,13 +63,15 @@ class Anologue extends \lithium\data\Model { */ public static function addMessage($id, $message = array()) { $anologue = static::find($id); - + + $message['text'] = Oembed::classify($message['text'], array('markdown' => true)); + if (!empty($message['email'])) { $message['email'] = md5($message['email']); } - + $message = $message + array('timestamp' => time()) + static::$_defaultMessage; - + if (!$anologue->messages) { $anologue->messages = array($message); } else { diff --git a/resources/tmp/cache/empty b/resources/tmp/cache/empty new file mode 100755 index 0000000..e69de29 diff --git a/resources/tmp/cache/templates/empty b/resources/tmp/cache/templates/empty new file mode 100644 index 0000000..e69de29 diff --git a/resources/tmp/logs/empty b/resources/tmp/logs/empty new file mode 100755 index 0000000..e69de29 diff --git a/tmp/cache/empty b/tmp/cache/empty deleted file mode 100644 index e69de29..0000000 diff --git a/tmp/logs/empty b/tmp/logs/empty deleted file mode 100755 index e69de29..0000000 diff --git a/views/anologue/view.html.php b/views/anologue/view.html.php index 35b48f9..910245b 100644 --- a/views/anologue/view.html.php +++ b/views/anologue/view.html.php @@ -11,7 +11,7 @@ <h1 class="smaller-title"> <?php echo $this->html->link( - 'anologue', + 'anologue', array('controller' => 'anologue', 'action' => 'index') ); ?> @@ -19,8 +19,8 @@ <h3 class="hash"> <?php echo $this->html->link( - $data->id, - array('controller' => 'anologue', 'action' => 'view', 'id' => $data->id), + $data->id, + array('controller' => 'anologue', 'action' => 'view', 'id' => $data->id), array('title' => 'Copy this url and give it to others') ); ?> @@ -52,7 +52,7 @@ </li> <li class="text"> <div class="markdown"> - <pre><?php echo $this->html->escape($message->text); ?></pre> + <pre><?php echo $h($message->text); ?></pre> </div> </li> </ul> @@ -85,7 +85,7 @@ <div class="about"> <?php echo $this->html->link( - 'what is anologue?', + 'what is anologue?', array('controller' => 'anologue', 'action' => 'index') ); ?> @@ -112,8 +112,8 @@ <audio id="anologue-speaker"></audio> <?php echo $this->html->script(array( - 'http://code.jquery.com/jquery-1.4.min.js', - 'md5.jquery.js', 'showdown.js', 'pretty.js', 'anologue.js', + 'http://code.jquery.com/jquery-1.4.2.min.js', + 'md5.jquery', 'showdown', 'pretty', 'jquery.oembed', 'anologue', )); ?> <script type="text/javascript" charset="utf-8"> $(document).ready(function() { @@ -121,7 +121,8 @@ id: '<?=$data->id?>', base: '<?php echo $this->_request->env('base') ?>', line: <?php echo count($data->messages); ?>, - icon: '<?php echo $avatar; ?>' + icon: '<?php echo $avatar; ?>', + intro: <?php echo (!empty($user['email']) || !empty($user['author'])) ? 'false' : 'true'; ?> }); }); </script> diff --git a/webroot/css/anologue.css b/webroot/css/anologue.css index 11d32d7..ba8dd09 100644 --- a/webroot/css/anologue.css +++ b/webroot/css/anologue.css @@ -53,6 +53,7 @@ p.last { padding:.5em 0 4em 0; } /** Anologue Messages **/ + .anologue h1, .anologue h2, .anologue h3, .anologue h4, .anologue h5, .anologue h6 { margin: .5em 0; font-family: Helvetica, Arial, sans-serif; text-transform: none; } .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; } @@ -70,8 +71,11 @@ p.last { padding:.5em 0 4em 0; } .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 .text .markdown ul, .anologue .text .markdown ol { margin: .5em 0; padding:0 1em 0 2em; font-size: 0.95em; } + .anologue .text .markdown li { margin: .25em 0;} + .anologue .text .markdown code { background:#454545; color:white; padding: .75em; -moz-box-shadow:0 0 4px black; -webkit-box-shadow: 0 0 4px black; box-shadow: 0 0 4px black; } + .anologue .text .markdown .oembed-container a { border-bottom: none; } + .anologue .text .markdown .oembed-container img, .anologue .text .markdown .oembed-container object { border: 1px solid #454545; -moz-box-shadow:0 0 4px black; -webkit-box-shadow: 0 0 4px black; box-shadow: 0 0 4px black; } /** 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; } diff --git a/webroot/index.php b/webroot/index.php index 6735be4..3b79fa0 100644 --- a/webroot/index.php +++ b/webroot/index.php @@ -2,23 +2,37 @@ /** * Lithium: the most rad php framework * - * @copyright Copyright 2009, Union of Rad, Inc. (http://union-of-rad.org) + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) * @license http://opensource.org/licenses/bsd-license.php The BSD License */ /** - * Welcome to Lithium! This front-controller file is the gateway to your application. It is - * responsible for intercepting requests, and handing them off to the Dispatcher for processing. + * Welcome to Lithium! This front-controller file is the gateway to your application. It is + * responsible for intercepting requests, and handing them off to the `Dispatcher` for processing. * * If you're sharing a single Lithium core install or other libraries among multiple - * applications, you may need to manually set things like LITHIUM_LIBRARY_PATH. You can do that in - * app/config/bootstrap.php, which is loaded below: + * applications, you may need to manually set things like `LITHIUM_LIBRARY_PATH`. You can do that in + * `config/bootstrap.php`, which is loaded below: */ require dirname(__DIR__) . '/config/bootstrap.php'; /** - * Dispatch a new request with the default settings. + * The following will instantiate a new `Request` object and pass it off to the `Dispatcher` class. + * By default, the `Request` will automatically aggregate all the server / environment settings, URL + * and query string parameters, request content (i.e. POST or PUT data), and HTTP method and header + * information. + * + * The `Request` is then used by the `Dispatcher` (in conjunction with the `Router`) to determine + * the correct controller to dispatch to, and the correct response type to render. The response + * information is then encapsulated in a `Response` object, which is returned from the controller + * to the `Dispatcher`, and finally echoed below. Echoing a `Response` object causes its headers to + * be written, and its response body to be written in a buffer loop. + * + * @see lithium\action\Request + * @see lithium\action\Response + * @see lithium\action\Dispatcher + * @see lithium\net\http\Router */ -echo lithium\action\Dispatcher::run(); +echo lithium\action\Dispatcher::run(new lithium\action\Request()); ?> \ No newline at end of file diff --git a/webroot/js/anologue.js b/webroot/js/anologue.js index 45532b3..92298db 100644 --- a/webroot/js/anologue.js +++ b/webroot/js/anologue.js @@ -3,12 +3,13 @@ var anologue = { id: 0, base : null, line: 0, - icon: null + icon: null, + intro: true, }, shiftDown: false, setup: function(config) { - this._config = config; + this._config = $.extend(this._config, config); $(".sound label").click(function() { anologue.toggleIcon('.sound'); }); @@ -26,7 +27,7 @@ var anologue = { anologue.markdownHelp(); return false; }); - + this.setupSubmit(); this.markdown(); this.fireworks(); @@ -34,7 +35,7 @@ var anologue = { this.listener(); this.humanizeTimes(); this.humanizeTimesTimer(); - + $('body').focusin(function() { anologue.resetTitle(); }); @@ -42,13 +43,17 @@ var anologue = { anologue.resetTitle(); }); }, - + fireworks: function() { - $("#anologue-help").animate({bottom: 0}, 2500); + if (this._config.intro) { + $("#anologue-help").animate({bottom: 0}, 2500); + } else { + this.closeHelp(); + } $("#anologue-speech-bar").animate({bottom: 0}, 1500); $("#anologue-author").focus(); }, - + setupSubmit: function() { $("#anologue-form").submit(function() { anologue.say(); @@ -141,13 +146,13 @@ var anologue = { var id = 'message-' + $.md5(message.timestamp + message.author); 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 if (message.author != user && user != '') { @@ -158,11 +163,11 @@ var anologue = { } } } - + if (message.author != user) { this.updateTitle(docTitle); } - + $('#'+id).animate({ opacity: 'show' }, 1000); @@ -207,10 +212,15 @@ var anologue = { var showdown = new Showdown.converter(); var text = showdown.makeHtml($(this).children('pre').html()); $(this).html(text).addClass('marked'); + $(this).find("a").oembed(null, { + embedMethod: 'annotate', + maxWidth: 425, + maxHeight: 425 + }); } }); }, - + toggleIcon: function(parentClass) { var disabled = $('.anologue-settings '+parentClass+' .icon').hasClass('disabled'); if (!disabled) { @@ -219,7 +229,7 @@ var anologue = { $(parentClass+' .icon').removeClass('disabled'); } }, - + closeHelp: function() { if (!$("#anologue-help").hasClass('closed')) { $("#anologue-help").animate({bottom: '-500px'}, 1000); @@ -227,14 +237,14 @@ var anologue = { } return false; }, - + getOption: function(parentClass) { var disabled = $('.anologue-settings '+parentClass+' .icon').hasClass('disabled'); return !disabled; }, - + humanizeTimesTimeout: null, - + humanizeTimesTimer: function() { clearTimeout(this.humanizeTimesTimeout); anologue.humanizeTimes(); @@ -242,7 +252,7 @@ var anologue = { anologue.humanizeTimesTimer(); }, 15000); }, - + humanizeTimes: function() { $('li.time').each(function() { var time = $(this).children('.timestamp').first().text(); @@ -250,19 +260,19 @@ var anologue = { $(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>'; @@ -280,11 +290,15 @@ var anologue = { this.showMarkdownHelp(); } }, - + showMarkdownHelp: function() { - $("#anologue-help").animate({bottom: 0}, 1000, function() { - $("#anologue-help").removeClass('closed'); - }); + if ($("#anologue-help").hasClass('closed')) { + $("#anologue-help").animate({bottom: 0}, 1000, function() { + $("#anologue-help").removeClass('closed'); + }); + } else { + this.closeHelp(); + } } - + } diff --git a/webroot/js/jquery.oembed.js b/webroot/js/jquery.oembed.js new file mode 100644 index 0000000..f6283d1 --- /dev/null +++ b/webroot/js/jquery.oembed.js @@ -0,0 +1,236 @@ +(function($) { + $.fn.oembed = function(url, options, callback) { + + options = $.extend(true, $.fn.oembed.defaults, options); + + return this.each(function() { + + var container = $(this), + resourceURL = (url != null) ? url : container.attr("href"), + provider; + + if (!callback) callback = function(container, oembed) { + $.fn.oembed.insertCode(container, options.embedMethod, oembed); + }; + + if (resourceURL != null) { + provider = getOEmbedProvider(resourceURL); + + if (provider != null) { + provider.params = getNormalizedParams(options[provider.name]) || {}; + provider.maxWidth = options.maxWidth; + provider.maxHeight = options.maxHeight; + provider.embedCode(container, resourceURL, callback); + return; + } + } + + callback(container, null); + }); + }; + + // Plugin defaults + $.fn.oembed.defaults = { + maxWidth: null, + maxHeight: null, + embedMethod: "replace" // "auto", "append", "fill" + }; + + $.fn.oembed.insertCode = function(container, embedMethod, oembed) { + if (oembed == null) + return; + switch(embedMethod) + { + case "auto": + if (container.attr("href") != null) { + insertCode(container, "append", oembed); + } + else { + insertCode(container, "replace", oembed); + }; + break; + case "replace": + container.replaceWith(oembed.code); + break; + case "fill": + container.html(oembed.code); + break; + case "append": + var oembedContainer = this.getOembedContainer(container, oembed); + oembedContainer.html(oembed.code); + break; + case "annotate": + if (!$(container).hasClass('open')) { + var oembedContainer = this.getOembedContainer(container, oembed); + oembedContainer.html(oembed.code); + container.before(oembedContainer); + container.addClass('open'); + } + break; + } + } + + $.fn.oembed.getOembedContainer = function(container, oembed) { + var oembedContainer = container.next(); + if (oembedContainer == null || !oembedContainer.hasClass("oembed-container")) { + oembedContainer = container + .after('<div class="oembed-container"></div>') + .next(".oembed-container"); + if (oembed != null && oembed.provider_name != null) + oembedContainer.toggleClass("oembed-container-" + oembed.provider_name); + } + return oembedContainer; + } + + $.fn.oembed.getPhotoCode = function(url, data) { + var alt = data.title ? data.title : ''; + alt += data.author_name ? ' - ' + data.author_name : ''; + alt += data.provider_name ? ' - ' +data.provider_name : ''; + var code = '<div><a href="' + url + '" target="_blank"><img src="' + data.url + '" alt="' + alt + '"/></a></div>'; + if (data.html) + code += "<div>" + data.html + "</div>"; + return code; + }; + + $.fn.oembed.getVideoCode = function(url, data) { + var code = data.html; + return code; + }; + + $.fn.oembed.getRichCode = function(url, data) { + var code = data.html; + return code; + }; + + $.fn.oembed.getGenericCode = function(url, data) { + var title = (data.title != null) ? data.title : url, + code = '<a href="' + url + '">' + title + '</a>'; + if (data.html) + code += "<div>" + data.html + "</div>"; + return code; + }; + + $.fn.oembed.isAvailable = function(url) { + var provider = getOEmbedProvider(url); + return (provider != null); + }; + + /* Private Methods */ + function getOEmbedProvider(url) { + for (var i = 0; i < providers.length; i++) { + if (providers[i].matches(url)) + return providers[i]; + } + return null; + } + + function getNormalizedParams(params) { + if (params == null) + return null; + var normalizedParams = {}; + for (var key in params) { + if (key != null) + normalizedParams[key.toLowerCase()] = params[key]; + } + return normalizedParams; + } + + var providers = [ + new OEmbedProvider("fivemin", "5min.com"), + new OEmbedProvider("amazon", "amazon.com"), + new OEmbedProvider("flickr", "flickr", "http://flickr.com/services/oembed", "jsoncallback"), + new OEmbedProvider("googlevideo", "video.google."), + new OEmbedProvider("hulu", "hulu.com"), + new OEmbedProvider("imdb", "imdb.com"), + new OEmbedProvider("metacafe", "metacafe.com"), + new OEmbedProvider("qik", "qik.com"), + new OEmbedProvider("revision3", "slideshare"), + new OEmbedProvider("slideshare", "5min.com"), + new OEmbedProvider("twitpic", "twitpic.com"), + new OEmbedProvider("viddler", "viddler.com"), + new OEmbedProvider("vimeo", "vimeo.com", "http://vimeo.com/api/oembed.json"), + new OEmbedProvider("wikipedia", "wikipedia.org"), + new OEmbedProvider("wordpress", "wordpress.com"), + new OEmbedProvider("youtube", "youtube.com"), + new OEmbedProvider("vids.myspace.com", "vids.myspace.com", "http://vids.myspace.com/index.cfm?fuseaction=oembed"), + new OEmbedProvider("screenr", "screenr.com", "http://screenr.com/api/oembed.json") + ]; + + function OEmbedProvider(name, urlPattern, oEmbedUrl, callbackparameter) { + this.name = name; + this.urlPattern = urlPattern; + this.oEmbedUrl = (oEmbedUrl != null) ? oEmbedUrl : "http://oohembed.com/oohembed/"; + this.callbackparameter = (callbackparameter != null) ? callbackparameter : "callback"; + this.maxWidth = 500; + this.maxHeight = 400; + + this.matches = function(externalUrl) { + // TODO: Convert to Regex + return externalUrl.indexOf(this.urlPattern) >= 0; + }; + + this.getRequestUrl = function(externalUrl) { + + var url = this.oEmbedUrl; + + if (url.indexOf("?") <= 0) + url = url + "?"; + else + url = url + "&"; + + var qs = ""; + + if (this.maxWidth != null && this.params["maxwidth"] == null) + this.params["maxwidth"] = this.maxWidth; + + if (this.maxHeight != null && this.params["maxheight"] == null) + this.params["maxheight"] = this.maxHeight; + + for (var i in this.params) { + // We don't want them to jack everything up by changing the callback parameter + if (i == this.callbackparameter) + continue; + + // allows the options to be set to null, don't send null values to the server as parameters + if (this.params[i] != null) + qs += "&" + escape(i) + "=" + this.params[i]; + } + + + url += "format=json&url=" + escape(externalUrl) + + qs + + "&" + this.callbackparameter + "=?"; + + return url; + } + + this.embedCode = function(container, externalUrl, callback) { + + var request = this.getRequestUrl(externalUrl); + + $.getJSON(request, function(data) { + + var oembed = $.extend(data); + + var code, type = data.type; + + switch (type) { + case "photo": + oembed.code = $.fn.oembed.getPhotoCode(externalUrl, data); + break; + case "video": + oembed.code = $.fn.oembed.getVideoCode(externalUrl, data); + break; + case "rich": + oembed.code = $.fn.oembed.getRichCode(externalUrl, data); + break; + default: + oembed.code = $.fn.oembed.getGenericCode(externalUrl, data); + break; + } + + callback(container, oembed); + }); + } + } +})(jQuery); \ No newline at end of file