CVE-2022-4592 (PHP, published 2022-12-18T08:15Z)
Description: value: A vulnerability was found in luckyshot CRMx and classified as critical. This issue affects the function get/save/delete/comment/commentdelete of the file index.php. The manipulation leads to sql injection. The attack may be initiated remotely. The name of the patch is 8c62d274986137d6a1d06958a6f75c3553f45f8f. It is recommended to apply a patch to fix this issue. The identifier VDB-216185 was assigned to this vulnerability.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
Repository: https://github.com/luckyshot/CRMx
Files changed:
.gitignore
.htaccess
LICENSE
README.md
SECURITY.md
config.php
crossdomain.xml
css/bootstrap.css
css/style.css
css/style.less
index.php
js/bootstrap-collapse.js
js/bootstrap-dropdown.js
js/crmx.js
js/jquery-easydate.js
js/script.js
lang/en_us.php
lang/es_es.php
limonade/limonade.php
limonade/limonade/abstract.php
limonade/limonade/assertions.php
limonade/limonade/lemons/lemon_mysql.php
limonade/limonade/tests.php
views/home.html.php
Commit message: FIX SQL Injection Vulnerabilities following report by pm_security_report

Vulnerable code:

Fixed code:

.DS_Store


<IfModule mod_rewrite.c>
  Options +FollowSymlinks
  Options +Indexes
  RewriteEngine on

  # if your app is in a subfolder
  # RewriteBase /my_app/ 

  # test string is a valid files
  RewriteCond %{SCRIPT_FILENAME} !-f
  # test string is a valid directory
  RewriteCond %{SCRIPT_FILENAME} !-d

  RewriteRule ^(.*)$   index.php?uri=/$1    [NC,L,QSA]
  # with QSA flag (query string append),
  # forces the rewrite engine to append a query string part of the
  # substitution string to the existing string, instead of replacing it.
</IfModule>
The MIT License (MIT)

Copyright (c) 2015 Xavi Esteve ( https://xaviesteve.com )

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
CRMx
===============
CRMx is a super-flexible micro-CRM system for personal, freelance and small businesses. It can be customized very quickly for Customer Relationship Management, Lead Management System, Project Management, To-Do List or any other usage due to its flexibility in customization and scalable code.

- <strong>RESTful API</strong>: Works through a RESTful API which allows third-party services and other software to interact neatly.
- <strong>Unlimited users & environments</strong>: Allows unlimited users to work in the same or different environments very flexibly (including a User Access Control system (UAC) to define permissions for each user and have maximum control).
- <strong>Simple setup</strong>: Similar to Sublime Text, all the config is done in <code>config.php</code> which keeps the overall code tiny and much easier to maintain and scale.
- <strong>Extremely scalable</strong>: CRMx is extremely small, with one PHP, one JS and one CSS files, with inline comments, you can see the code and start hacking it in 5 minutes. Oh, and you can also create CRMx plugins :)



Screenshots
---------------

### Table view

From here you have an overall view of your contacts:

![Table view](http://i.imgur.com/U6krA3U.png)

### Search as-you-type

Type in the search box to start filtering, results update dynamically as you type:

![Search as-you-type](http://i.imgur.com/5v3kgpQ.png)

### Smart filtering

CRMx detects your custom form field types and adds shortcut lists on the top navigation automatically:

![Smart filtering](http://i.imgur.com/H6Ur7IR.png)

### Person view

View a person details, edit them directly and also add timed comments:

![Person view](http://i.imgur.com/bAeyQHu.png)

### Smart links

Buttons appear next to the person fields to email, search Google or Skype-call in one click

![Smart links](http://i.imgur.com/W4Nj2LJ.png)


Technology
---------------

- Limonade PHP micro-framework (inc. <a href="https://gist.github.com/luckyshot/5098146">custom MySQL <i>lemon</i></a>)
- MySQL
- JavaScript / jQuery / JSON
- Twitter Bootstrap (and LESS)



Settings
---------------

There is no Settings menu or user accounts, all is done in PHP variables (like Sublime Text) in the <code>config.php</code> file, which makes the code app a lot smaller as well as easy to administer, maintain and scale.



Installation
---------------

Open <code>config.php</code> to modify the app settings:

- Type in the MySQL user, password, server and prefix (There is no need to create the MySQL tables, these are created automatically)
- Customize the <code>$form</code> array (see Form field types below for more info)
- Add your <code>$users</code> and their permissions
- If you are installing CRMx in a subdomain:
  1. Modify the <code>option('base_uri', '/');</code> and add it there (for <code>example.com/crmx/</code> it should be <code>option('base_uri', '/crmx/');</code>, note the trailing slash)
  2. Do the same in the <code>.htaccess</code> file, uncomment the <code>#</code> and add the subdomain (following the example above it will be <code>RewriteBase /crmx/</code>)
- Open CRMx and type in your user's password. You can also bookmark <code>http://YOURCRMXPATH.com<b>/login/YOURPASSWORD</b></code> so that it autologins you every time.



User accounts
---------------

User accounts are created by adding them to the <code>$users</code> PHP array. These are the fields you can customize:

- <strong>name</strong> <small>(string)</small> Full name of the user
- <strong>pass</strong> <small>(string)</small> Add a very long random alphanumeric string of between 100 and 300 characters (the more the better, check <code>is_logged_in()</code> in the code for a generator)
- <strong>level</strong> <small>(string)</small> Add flags to allow users certain privileges
	- <strong>r</strong> <i>read</i>: useful when you want an external partner to submit contacts but not be able to access the CRM (i.e. external lead provider)
	- <strong>s</strong> <i>save</i>: create and update contacts
	- <strong>d</strong> <i>delete</i>: can delete contacts
	- <strong>c</strong> <i>comment</i>: can comment on contacts
- <strong>dbprefix</strong> <small>(string)</small> Many users can work in different environments on the same database by using a different table. To do this, just specify a different MySQL prefix here (i.e. <code>sales_</code>)
- <strong>sitename</strong> <small>(string)</small> You can customize the app title for each user


### Logging in

There is no login screen in CRMx. Users bookmark a long URL and click on it to login. You should specify a long and unique password (at least 50 characters) for each user and then send them the URL to bookmark, which looks like:

<code>http://crmx.com/login/thesuperlongpassword</code>



Environments
---------------

Environments allow users to work on separated CRMx (with their own contacts and form fields) while using the same app. Add that prefix to a user and another array in <code>$form</code>. Simple as that.




Form field types
---------------

It's very easy to customize CRMx to your own needs. You just need to modify the <code>$form</code> PHP array in <code>config.php</code> and the app will take care of the rest.


### Textbox

Just name and title are needed:

<pre>'name' => 'email',
'title' => 'Email address'</pre>


### Select dropdown

Specify <code>'type' => 'select'</code> and a list of elements:

<pre>'name' => 'color',
'title' => 'Favorite color',
'type' => 'select',
'list' => array( "Red", Green", "Blue" )</pre>


### Others formats

You can use other HTML5 form field types like: <code>password</code>, <code>hidden</code>, <code>color</code>, <code>date</code>, <code>datetime</code>, <code>datetime-local</code>, <code>email</code>, <code>month</code>, <code>number</code>, <code>range</code>, <code>search</code>, <code>tel</code>, <code>time</code>, <code>url</code> and <code>week</code>.

<pre>'name' => 'website',
'title' => 'Website URL',
'type' => 'url'</pre>

### Example

This is how I have my personal CRMx <code>config.php</code> set up:

<pre>$form = array(
	'test_' => array( // table prefix
		// name (default, no need to specify)
		// title (default, no need to specify)
		array('name' => 'group',	'title' => 'Group',			'type' => 'select',	'list' => array( "-", "London", "Barcelona")),
		array('name' => 'type',		'title' => 'Type',			'type' => 'select',	'list' => array( "-", "Partner", "Client", "Lead")),
		array('name' => 'email',	'title' => 'Email',			'type' => 'email'),
		array('name' => 'phone',	'title' => 'Phone Number',	'type' => 'tel'),
		array('name' => 'company',	'title' => 'Company',		'type' => 'search'),
		array('name' => 'address',	'title' => 'Address',		'type' => 'search'),
	),
);</pre>


## Hidden

To skip a form field to show in the main table, set the <code>hidden</code> property to <code>1</code>. This is useful when you have a lot of fields or you want to disable a field you might want to use in the future.

<pre>'hidden' => 1,</pre>

### Deleting fields

Note that if you remove a field from <code>config.php</code> it will still be in the database and will disappear when that person is updated. If you'd rather not loose any info, set <code>hidden = 1</code> instead of deleting it.

REST API
---------------

### Home <code>/</code>

##### Request data (<code>GET</code>)

<i>(none)</i>

##### Response (<code>HTML</code>)

The home page in HTML format (including the default people and form JSON lists embedded to save server requests).



<hr>



### Login <code>/login/:pass</code>

##### Request data (GET)

- <code>pass</code> (string)

##### Response (<code>JSON</code>)

On success redirects to Home, on fail shows a message.



<hr>



### Search <code>/search/:q</code>

Searches people for that query and returns a JSON array.

##### Request data (<code>GET</code>)

- <code>q</code> (string)

##### Response <code>JSON</code>

<pre>{
    "id":"46",
    "name":"Richard",
    "form":{
      "title":"CEO",
      // your defined form fields
    }
  },{
    "id":"37",
    "name":"Peter",
    "form":{
      "title":"Director",
      // your defined form fields
    }
}</pre>



<hr>



### Load person <code>/get/:id</code>

You can pass an ID or a name, returns results for a single person (if more than one match returns the most recently modified).

##### Request data (<code>GET</code>)

- <code>id</code> (string)

##### Response (<code>JSON</code>)

<pre>{
  "id": "46",
  "name": "Richard",
  "form": {
    "title": "CEO",
    // your defined form fields
  },
  "comments":[
    {
      "user": "Xavi Esteve",
      "date": "2013-03-24T16:03:19+00:00",
      "text": "..."
    }
  ],
  "created": "1364140289",
  "updated": "1364140289"
}</pre>



<hr>



### Save person <code>/save</code>

##### Request data (<code>POST</code>)

- <code>id</code> (string)

##### Response (<code>JSON</code>)

<pre>{
    "status": "success" OR "error",
    "message": "Contact saved successfully."
}</pre>



<hr>



### Delete person <code>/delete</code>

##### Request data (<code>DELETE</code>)

- <code>id</code> (string)

##### Response (<code>JSON</code>)

<pre>{
    "status": "success" OR "error",
    "message": "Contact deleted successfully."
}</pre>



<hr>



### Add comment <code>/comment</code>

##### Request data (<code>POST</code>)

- <code>id</code> (integer)
- <code>comment</code> (string)

##### Response (<code>JSON</code>)

<pre>{
    "status": "success" OR "error",
    "message": "Comment added."
}</pre>



<hr>



### Delete comment <code>/comment/:id</code>

##### Request data (<code>DELETE</code>)

- <code>id</code> (integer)

##### Response (<code>JSON</code>)

<pre>{
    "status": "success" OR "error",
    "message": "Comment deleted."
}</pre>






Plugins
--------------

With plugins you can add extra functionality to CRMx without needing to modify the core files. Creating plugins is extremely easy and you can run PHP, JavaScript and/or CSS code. To create a plugin, add a new folder to the <code>plugins</code> folder and files, all with your plugin name:

<pre>/plugins/salesforce
	salesforce.php
	salesforce.js
	salesforce.css</pre>

All files are optional, if you want to create a theme then a single CSS file should be enough. If you want to have a file that doesn't run automatically then name it differently than the plugin name.

The last step is to add the plugin name to the <code>$plugins</code> array in <code>config.php</code>.



Multi-language
---------------

In the <code>lang</code> folder, create a new language file (use <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" target="_blank" rel="nofollow">ISO 639-1 codes</a>) or use an existing one.

Then, add <code>'lang' => 'es_es',</code> in the <strong>user's configuration</strong>.

Alternatively, you can change <code>LANG_DEFAULT</code> so everyone will have the same language.

Having different languages is not only useful to change the language of CRMx but to customize the app further. For example, if you use CRMx as a Project Plan system you can rename 'Name' to 'Project' or 'Save contact' to 'Save project'.



MySQL table details
---------------

### <code>people</code> table

(You don't need to create any tables, CRMx will create them automatically)

- <strong>id</strong> <small>(int20, primary, autoincrement)</small>
- <strong>name</strong> <small>(string255, mandatory)</small>
- <strong>form</strong> <small>(text, json)</small>
- <strong>comments</strong> <small>(text, json)</small>
- <strong>created</strong> <small>(int11)</small>
- <strong>updated</strong> <small>(int11)</small>








Changelog
----------------

### 4 April 2013

- Fixed sorting Titles (thanks <a href="https://github.com/soomiq" rel="nofollow">Soomiq</a>)
- Fixed Language files
- Fixed Save/New contact button
- Added sprite images


### 1 April 2013
- Multilanguage
- Plugins
- Code clean up and improvements
- Favicon


### 29 March 2013

- Generate MySQL tables automatically
- Multiword search
- Sort by column
- Delete comments
- Code optimization
- Code documentation and inline comments


### 28 March 2013

- CRMx plugins to add extra functionality (canned responses, SalesForce integration, etc.)
- Table view instead of Sidebar
- Redesigned top nav
- Search also searches comments now
- Added icons and buttons
- Form is now two-column instead of one
- Smart links in detail view
- Improved docs, added screenshots
- Many more improvements and small tweaks
- Fixed bugs with notification message
- Other bugs fixed
- Reorganized all files
- Minified all JS into one file
- jQuery to use latest instead of 1.8.6
- Minified CSS into two files
- Responsive improvements
- MIT licensed



To Do
----------------
- Use same date format along MySQL
- Smooth scrolling anchors up/down page
- Write tests


License
----------------

CRMx has been created by Xavi Esteve and is licensed under a MIT License.

Copyright &copy; 2013 Xavi Esteve

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Credits
---------------

Author: <strong><a href="https://xaviesteve.com/" target="_blank">Xavi Esteve</a></strong> (<a href="https://twitter.com/xaviesteve" target="_blank" rel="nofollow">@xaviesteve</a>)

- Icons by <a href="http://glyphicons.com/" target="_blank" rel="nofollow">Glyphicons</a> (attribution)
- <a href="http://twitter.github.com/bootstrap/" target="_blank" rel="nofollow">Twitter Bootstrap</a> by Twitter, Inc (Apache license)
- <a href="http://limonade-php.github.com/" target="_blank" rel="nofollow">Limonade PHP micro framework</a> by Fabrice Luraine (MIT license)
- <a href="http://easydate.parshap.com/" target="_blank" rel="nofollow">EasyDate</a> by Parsha Pourkhomami (MIT license)
- <a href="https://github.com/bryanwoods/autolink-js" target="_blank" rel="nofollow">AutoLink</a> by Bryan Woods (open sourced)

# Security Policies and Procedures

This document outlines security procedures and general policies for the 
project.

  * [Reporting a Vulnerability](#reporting-a-vulnerability)
  * [Disclosure Policy](#disclosure-policy)

## Reporting a Vulnerability 

The team and community take all security vulnerabilities seriously. Thank 
you for improving the security of our open source software. We appreciate 
your efforts and responsible disclosure and will make every effort to 
acknowledge your contributions.

Report security vulnerabilities by emailing the team at:
    
    security at xaviesteve.com

The lead maintainer will acknowledge your email within 24-72 hours. 
After the initial reply to your report, the team will endeavor to keep 
you informed of the progress towards a fix and full announcement, 
and may ask for additional information or guidance.

Report security vulnerabilities in third-party modules to the person or 
team maintaining the module.

## Disclosure Policy

When the security team receives a security bug report, they will assign it
to a primary handler. This person will coordinate the fix and release
process, involving the following steps:

  * Confirm the problem and determine the affected versions.
  * Audit code to find any potential similar problems.
  * Prepare fixes for all releases still under maintenance. These fixes
    will be released as fast as possible to the repository.
<?php
/*
	,o888888o.    8 888888888o.            ,8.       ,8.          `8.`8888.      ,8'
   8888     `88.  8 8888    `88.          ,888.     ,888.          `8.`8888.    ,8'
,8 8888       `8. 8 8888     `88         .`8888.   .`8888.          `8.`8888.  ,8'
88 8888           8 8888     ,88        ,8.`8888. ,8.`8888.          `8.`8888.,8'
88 8888           8 8888.   ,88'       ,8'8.`8888,8^8.`8888.          `8.`88888'
88 8888           8 888888888P'       ,8' `8.`8888' `8.`8888.         .88.`8888.
88 8888           8 8888`8b          ,8'   `8.`88'   `8.`8888.       .8'`8.`8888.
`8 8888       .8' 8 8888 `8b.       ,8'     `8.`'     `8.`8888.     .8'  `8.`8888.
   8888     ,88'  8 8888   `8b.    ,8'       `8        `8.`8888.   .8'    `8.`8888.
	`8888888P'    8 8888     `88. ,8'         `         `8.`8888. .8'      `8.`8888.

Copyright (c) 2013 Xavi Esteve

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

option('base_uri', '/'); // '/' or same as the RewriteBase in your .htaccess

date_default_timezone_set('Europe/London');
ini_set('session.gc_maxlifetime', 3600); // seconds (3600 = 1 hour)

define("SITE_NAME", "CRMx"); // Default sitename when none specified in environments
define("SITE_VERSION", "0.3.1");

define("MYSQL_SERVER", "localhost");
define("MYSQL_USER", "user");
define("MYSQL_PASS", "pass");
define("MYSQL_DATABASE", "crmx");

define("LANG_DEFAULT", "en_us"); // Default language


// Plugin list
$plugins = array(/*'test'*/);


// Environments
$form = array(
	'test_' => array( // table prefix
		// name (default, no need to specify)
		// title (default, no need to specify)
		array('name' => 'group',	'title' => 'Group',			'type' => 'select',	'list' => array( "-", "London", "Barcelona")),
		array('name' => 'type',		'title' => 'Type',			'type' => 'select',	'list' => array( "-", "Partner", "Client", "Lead")),
		array('name' => 'email',	'title' => 'Email',			'type' => 'email'),
		array('name' => 'phone',	'title' => 'Phone Number',	'type' => 'tel'),
		array('name' => 'company',	'title' => 'Company',		'type' => 'search'),
		array('name' => 'address',	'title' => 'Address',		'type' => 'search'),
	),
);


// Users
$users = array(
	// TEST_
	array(
		'name' => 'Tester',
		'pass' => 'test',
		'level' => 'rsdc',
		'dbprefix' => 'test_',
		'sitename' => 'CRMx Example',
	),
);
<?xml version="1.0" ?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
/*!
 * Bootstrap v2.1.1
 *
 * Copyright 2012 Twitter, Inc
 * Licensed under the Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Designed and built with all the love in the world @twitter by @mdo and @fat.
 */article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{max-width:100%;width:auto\9;height:auto;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;content:"";line-height:0}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.127659574468085%;*margin-left:2.074468085106383%}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;content:"";line-height:0}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;content:"";line-height:0}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}.text-warning{color:#c09853}.text-error{color:#b94a48}.text-info{color:#3a87ad}.text-success{color:#468847}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:1;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1{font-size:36px;line-height:40px}h2{font-size:30px;line-height:40px}h3{font-size:24px;line-height:40px}h4{font-size:18px;line-height:20px}h5{font-size:14px;line-height:20px}h6{font-size:12px;line-height:20px}h1 small{font-size:24px}h2 small{font-size:18px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;content:"";line-height:0}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:25px}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:9px;font-size:14px;line-height:20px;color:#555;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;*margin-top:0;margin-top:1px \9;line-height:normal;cursor:pointer}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;border:1px solid #ccc;background-color:#fff}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);cursor:not-allowed}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:18px;padding-left:18px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;content:"";line-height:0}.controls-row:after{clear:both}.controls-row [class*="span"]{float:left}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info>label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;content:"";line-height:0}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px}.input-append,.input-prepend{margin-bottom:5px;font-size:0;white-space:nowrap}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;font-size:14px;vertical-align:top;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .add-on,.input-append .btn{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;*zoom:1;margin-bottom:0;vertical-align:middle}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";line-height:0}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child,.table-bordered tfoot:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child,.table-bordered tfoot:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topleft:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table-hover tbody tr:hover td,.table-hover tbody tr:hover th{background-color:#f5f5f5}table [class*=span],.row-fluid table [class*=span]{display:table-cell;float:none;margin-left:0}.table .span1{float:none;width:44px;margin-left:0}.table .span2{float:none;width:124px;margin-left:0}.table .span3{float:none;width:204px;margin-left:0}.table .span4{float:none;width:284px;margin-left:0}.table .span5{float:none;width:364px;margin-left:0}.table .span6{float:none;width:444px;margin-left:0}.table .span7{float:none;width:524px;margin-left:0}.table .span8{float:none;width:604px;margin-left:0}.table .span9{float:none;width:684px;margin-left:0}.table .span10{float:none;width:764px;margin-left:0}.table .span11{float:none;width:844px;margin-left:0}.table .span12{float:none;width:924px;margin-left:0}.table .span13{float:none;width:1004px;margin-left:0}.table .span14{float:none;width:1084px;margin-left:0}.table .span15{float:none;width:1164px;margin-left:0}.table .span16{float:none;width:1244px;margin-left:0}.table .span17{float:none;width:1324px;margin-left:0}.table .span18{float:none;width:1404px;margin-left:0}.table .span19{float:none;width:1484px;margin-left:0}.table .span20{float:none;width:1564px;margin-left:0}.table .span21{float:none;width:1644px;margin-left:0}.table .span22{float:none;width:1724px;margin-left:0}.table .span23{float:none;width:1804px;margin-left:0}.table .span24{float:none;width:1884px;margin-left:0}.table tbody tr.success td{background-color:#dff0d8}.table tbody tr.error td{background-color:#f2dede}.table tbody tr.warning td{background-color:#fcf8e3}.table tbody tr.info td{background-color:#d9edf7}.table-hover tbody tr.success:hover td{background-color:#d0e9c6}.table-hover tbody tr.error:hover td{background-color:#ebcccc}.table-hover tbody tr.warning:hover td{background-color:#faf2cc}.table-hover tbody tr.info:hover td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;margin-top:1px}.icon-white,.nav-tabs>.active>a>[class^="icon-"],.nav-tabs>.active>a>[class*=" icon-"],.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{background-position:-216px -120px;width:16px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{background-position:-408px -120px;width:16px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu li>a:focus,.dropdown-submenu:hover>a{text-decoration:none;color:#fff;background-color:#08c;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;outline:0;background-color:#08c;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu .disabled>a,.dropdown-menu .disabled>a:hover{color:#999}.dropdown-menu .disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#ccc;margin-top:5px;margin-right:-10px}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown .dropdown-menu .nav-header{padding-left:20px;padding-right:20px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 14px;margin-bottom:0;font-size:14px;line-height:20px;*line-height:20px;text-align:center;vertical-align:middle;cursor:pointer;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#e6e6e6;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #bbb;*border:0;border-bottom-color:#a2a2a2;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*margin-left:.3em;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:2px}.btn-small{padding:3px 9px;font-size:12px;line-height:18px}.btn-small [class^="icon-"]{margin-top:0}.btn-mini{padding:2px 6px;font-size:11px;line-height:17px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#c5c5c5;border-color:rgba(0,0,0,0.15) rgba(0,0,0,0.15) rgba(0,0,0,0.25)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#f89406;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#bd362f;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#51a351;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#2f96b4;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#222;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{border-color:transparent;cursor:pointer;color:#08c;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover{color:#333;text-decoration:none}.btn-group{position:relative;font-size:0;vertical-align:middle;white-space:nowrap;*margin-left:.3em}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{font-size:0;margin-top:10px;margin-bottom:10px}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-toolbar .btn+.btn,.btn-toolbar .btn-group+.btn,.btn-toolbar .btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu{font-size:14px}.btn-group>.btn-mini{font-size:11px}.btn-group>.btn-small{font-size:12px}.btn-group>.btn-large{font-size:16px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);*padding-top:5px;*padding-bottom:5px}.btn-group>.btn-mini+.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:2px;*padding-bottom:2px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{padding-left:12px;padding-right:12px;*padding-top:7px;*padding-bottom:7px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-mini .caret,.btn-small .caret,.btn-large .caret{margin-top:6px}.btn-large .caret{border-left-width:5px;border-right-width:5px;border-top-width:5px}.dropup .btn-large .caret{border-bottom:5px solid #000;border-top:0}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical .btn{display:block;float:none;width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical .btn+.btn{margin-left:0;margin-top:-1px}.btn-group-vertical .btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical .btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical .btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical .btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847}.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-left:0;margin-bottom:20px;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";line-height:0}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px}.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{border-top-color:#08c;border-bottom-color:#08c;margin-top:6px}.nav .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:"";line-height:0}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover{text-decoration:none;background-color:transparent;cursor:default}.navbar{overflow:visible;margin-bottom:20px;color:#777;*position:relative;*z-index:2}.navbar-inner{min-height:40px;padding-left:20px;padding-right:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065);*zoom:1}.navbar-inner:before,.navbar-inner:after{display:table;content:"";line-height:0}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar .brand{float:left;display:block;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-left:1px solid #f2f2f2;border-right:1px solid #fff}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:"";line-height:0}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{margin-bottom:0;padding:4px 14px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;width:100%;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1),0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1),0 1px 10px rgba(0,0,0,0.1);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.1),0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,0.1),0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 0 rgba(0,0,0,0.1),0 -1px 10px rgba(0,0,0,0.1);box-shadow:inset 0 1px 0 rgba(0,0,0,0.1),0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{background-color:transparent;color:#333;text-decoration:none}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#e5e5e5;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute;top:-7px;left:9px}.navbar .nav>li>.dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:10px}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0,0,0,0.2);border-bottom:0;bottom:-7px;top:auto}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{border-top:6px solid #fff;border-bottom:0;bottom:-6px;top:auto}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:#e5e5e5;color:#555}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{left:auto;right:0}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{left:auto;right:12px}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{left:auto;right:13px}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{left:auto;right:100%;margin-left:0;margin-right:-1px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse{color:#999}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0);border-color:#252525}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover{color:#fff}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{background-color:transparent;color:#fff}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .divider-vertical{border-left-color:#111;border-right-color:#222}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{background-color:#111;color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15);outline:0}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#040404;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #fff}.breadcrumb .divider{padding:0 5px;color:#ccc}.breadcrumb .active{color:#999}.pagination{height:40px;margin:20px 0}.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:0 14px;line-height:38px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover{color:#999;background-color:transparent;cursor:default}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin:20px 0;list-style:none;text-align:center;*zoom:1}.pager:before,.pager:after{display:table;content:"";line-height:0}.pager:after{clear:both}.pager li{display:inline}.pager a,.pager span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a,.pager .next span{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover,.pager .disabled span{color:#999;background-color:#fff;cursor:default}.modal-open .modal .dropdown-menu{z-index:2050}.modal-open .modal .dropdown.open{*z-index:2050}.modal-open .modal .popover{z-index:2060}.modal-open .modal .tooltip{z-index:2080}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out;top:-25%}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{overflow-y:auto;max-height:400px;padding:15px}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff;*zoom:1}.modal-footer:before,.modal-footer:after{display:table;content:"";line-height:0}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-3px}.tooltip.right{margin-left:3px}.tooltip.bottom{margin-top:3px}.tooltip.left{margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;width:236px;padding:1px;background-color:#fff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-bottom:10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-right:10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.popover .arrow,.popover .arrow:after{position:absolute;display:inline-block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow:after{content:"";z-index:-1}.popover.top .arrow{bottom:-10px;left:50%;margin-left:-10px;border-width:10px 10px 0;border-top-color:#fff}.popover.top .arrow:after{border-width:11px 11px 0;border-top-color:rgba(0,0,0,0.25);bottom:-1px;left:-11px}.popover.right .arrow{top:50%;left:-10px;margin-top:-10px;border-width:10px 10px 10px 0;border-right-color:#fff}.popover.right .arrow:after{border-width:11px 11px 11px 0;border-right-color:rgba(0,0,0,0.25);bottom:-11px;left:-1px}.popover.bottom .arrow{top:-10px;left:50%;margin-left:-10px;border-width:0 10px 10px;border-bottom-color:#fff}.popover.bottom .arrow:after{border-width:0 11px 11px;border-bottom-color:rgba(0,0,0,0.25);top:-1px;left:-11px}.popover.left .arrow{top:50%;right:-10px;margin-top:-10px;border-width:10px 0 10px 10px;border-left-color:#fff}.popover.left .arrow:after{border-width:11px 0 11px 11px;border-left-color:rgba(0,0,0,0.25);bottom:-11px;right:-1px}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:"";line-height:0}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto}.thumbnail .caption{padding:9px;color:#555}.label,.badge{font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;vertical-align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.progress .bar{width:0;height:100%;color:#fff;float:left;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{overflow:hidden;width:100%;position:relative}.carousel .item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{left:auto;right:15px}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{color:#fff;line-height:20px}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px}.hero-unit p{font-size:18px;font-weight:200;line-height:30px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}
 
/*!
 * Bootstrap Responsive v2.3.1
 *
 * Copyright 2012 Twitter, Inc
 * Licensed under the Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Designed and built with all the love in the world @twitter by @mdo and @fat.
 */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
/*! CRMx - Copyright (c) 2013 Xavi Esteve (MIT License) */.tr{text-align:right}body{font-size:12px;padding-top:60px;padding-bottom:40px}.sidebar-nav{padding:9px 0}.nav-list a{color:#333;font-weight:700}.nav-list a small{color:#999;display:block;font-weight:200;margin-top:-6px}.nav-list .active a small{color:#eee}#name{border:0;box-shadow:none;font-weight:bold;font-size:36px;text-rendering:optimizelegibility;height:auto;color:#333;margin:0;line-height:1em;width:100%}.lead{margin:0}#title{border:0;box-shadow:none;font-size:21px;font-weight:200;height:auto;padding:.3em;width:100%}#notification{cursor:pointer;position:fixed;opacity:.9;top:41px;width:100%;left:0;padding:0 20px 0 20px}#notification strong{text-transform:capitalize}#people-table a{display:block}#people-table a:hover{text-decoration:none}#people-table strong{font-size:115%}#people-table th{cursor:pointer}#people-table th.active::after{content:" \25BE"}#main{padding-top:60px}#form .control-group{margin-left:0}#form .input-append input{max-width:65%}label{margin-bottom:0;font-size:90%}#c{height:6em;transition:.3s ease height}#comments{margin-top:2em}.comment-delete{color:#bd362f;text-decoration:none}footer{margin-top:400px}@media(max-width:980px){body{padding-top:0}.navbar-text.pull-right{float:none;padding-left:5px;padding-right:5px}#notification{top:50px}}@media(max-height:600px){footer{margin-top:100px}}@media(min-height:800px){footer{margin-top:600px}}
/*! CRMx - Copyright (c) 2013 Xavi Esteve (MIT License) */

.tr {text-align: right;}

body {font-size:12px;padding-top: 60px;padding-bottom: 40px;}
	.sidebar-nav {padding: 9px 0;}

.nav-list a {color:#333;font-weight:700;}
	.nav-list a small {color:#999;display: block;font-weight:200;margin-top: -6px;}
	.nav-list .active a small {color:#eee;}


/* header */
#name {border:none;box-shadow:none;font-weight: bold;font-size: 36px;text-rendering: optimizelegibility;height: auto;color: #333;margin: 0;line-height: 1em;width: 100%;}
.lead {margin:0;}
#title {border:none;box-shadow:none;font-size: 21px;font-weight: 200;height:auto;padding:.3em;width: 100%;}




/* main */
#notification {cursor: pointer;position: fixed;opacity: .9;top: 41px;width: 100%;left: 0;padding: 0 20px 0 20px;}
#notification strong {text-transform: capitalize;}

#people-table a {display: block;}
#people-table a:hover {text-decoration: none;}
#people-table strong {font-size:115%;}
#people-table th {cursor:pointer;}​
#people-table th.active::after {content:" \25BE";}​

#main {padding-top:60px;}

#form .control-group {margin-left: 0;}
#form .input-append input {max-width: 65%;}
label {margin-bottom: 0;font-size: 90%;}


#c {height: 6em;transition: .3s ease height;}
#comments {margin-top:2em;}
.comment-delete {color: #bd362f;text-decoration:none;}


footer {margin-top:400px;}

@media (max-width: 980px) {
	body {padding-top: 0}
	/* Enable use of floated navbar text */
	.navbar-text.pull-right {float: none;padding-left: 5px;padding-right: 5px;}
	#notification {top: 50px;}
	#people-table td:nth-child(1n+6),
	#people-table th:nth-child(1n+6) {display:none;}
}

@media (max-width: 640px) {
	#people-table td:nth-child(1n+3),
	#people-table th:nth-child(1n+3) {display:none;}
}


@media (max-height: 600px) {
	footer{margin-top:100px;}
}
@media (min-height: 800px) {
	footer{margin-top:600px;}
}




<?php
/*
	,o888888o.    8 888888888o.            ,8.       ,8.          `8.`8888.      ,8'
   8888     `88.  8 8888    `88.          ,888.     ,888.          `8.`8888.    ,8'
,8 8888       `8. 8 8888     `88         .`8888.   .`8888.          `8.`8888.  ,8'
88 8888           8 8888     ,88        ,8.`8888. ,8.`8888.          `8.`8888.,8'
88 8888           8 8888.   ,88'       ,8'8.`8888,8^8.`8888.          `8.`88888'
88 8888           8 888888888P'       ,8' `8.`8888' `8.`8888.         .88.`8888.
88 8888           8 8888`8b          ,8'   `8.`88'   `8.`8888.       .8'`8.`8888.
`8 8888       .8' 8 8888 `8b.       ,8'     `8.`'     `8.`8888.     .8'  `8.`8888.
   8888     ,88'  8 8888   `8b.    ,8'       `8        `8.`8888.   .8'    `8.`8888.
	`8888888P'    8 8888     `88. ,8'         `         `8.`8888. .8'      `8.`8888.

Copyright (c) 2013 Xavi Esteve (MIT License)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/



require_once('limonade/limonade.php');
require_once('limonade/limonade/lemons/lemon_mysql.php');
require_once('config.php');


/********************************************************
 * CONFIG
 *******************************************************/
// error_reporting(E_ALL);
// option('debug', true);




$c = mysqli_connect(MYSQL_SERVER, MYSQL_USER, MYSQL_PASS, MYSQL_DATABASE);




/********************************************************
 * ROUTERS
 *******************************************************/

dispatch('/login/:pass', 'login');
dispatch('/logout', 'logout');

dispatch('/', 'home');

// People
dispatch('/search/:q', 'search');

// Person
dispatch('/get/:detail', 'get');
dispatch_post('/save', 'save');
dispatch_delete('/delete/:id', 'delete');

dispatch_post('/comment', 'comment');
dispatch_delete('/comment/:id', 'commentdelete');

// for API usage
dispatch('/api/:pass/:action/:detail', 'api');




/********************************************************
 * MODELS
 *******************************************************/

/**
 * login
 * Authenticate user
 * @param (string) User's password
 * @param (bool) Set as false to avoid redirect to the home page
 */
function login($pass, $redirect = true) {
	global $users, $form, $c;

	foreach ($users as $key => $user) {
		if ($pass==$user['pass'] && $form[$user['dbprefix']]) { // pass correct & environment exists
			$_SESSION['name'] = $user['name'];
			$_SESSION['level'] = $user['level'];
			$_SESSION['dbprefix'] = $user['dbprefix'];
			if (isset($user['lang'])) {
				$_SESSION['lang'] = $user['lang'];
			}else{
				$_SESSION['lang'] = LANG_DEFAULT;
			}
			if (isset($user['sitename'])) {
				$_SESSION['sitename'] = $user['sitename'];
			}
			$_SESSION['id'] = $key;

			$q = 'CREATE TABLE IF NOT EXISTS `'.$user['dbprefix'].'people` (
				`id` int(20) NOT NULL AUTO_INCREMENT,
				`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
				`form` text COLLATE utf8_unicode_ci,
				`comments` text COLLATE utf8_unicode_ci,
				`created` int(11),
				`updated` int(11),
				PRIMARY KEY (`id`)
			) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;';

			$result = db($q, $c);

			if ( $result === false ){ echo 'Could not create the Database tables, please check your MySQL settings.'; }

			if ($redirect==true) {
				header("Location: ".url_for('/')); // send_header
				return true;
			}else{
				return true;
			}
		}
	}
	return false;
}






/**
 * logout
 * Close user's session
 * @param ()
 */
function logout() {
	session_destroy();
	redirect_to("Location: ".url_for('')); // send_header
	halt();
}





/**
 * api
 * Authenticates and executes a request all-in-one
 * @param (string) User's password
 * @param (string) Action
 * @param (string) Details of the action
 */
function api ($pass, $action, $detail) {
	if (!login($pass, false)) {
		return json(array('status'=>'error','message'=>$lang['authfailed']));
	}
	// TODO: There may be a better way to do this and not repeat ourselves
	if ($action === 'search') { return search($detail);
	}else if ($action === 'get') { return get($detail);
	}else if ($action === 'save') { return save();
	}else if ($action === 'delete') { return delete($detail);
	}else if ($action === 'comment') { return comment($detail);
	}else{ return json(array('status'=>'error','message'=>$lang['actionnotrecognized'])); }
}







/**
 * home
 * Load the home page as HTML
 */
function home() {
	global $lang;
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 'r')===-1) {
		die('<!doctype html><html><body style="font-family: \'Helvetica Neue\',Helvetica,Arial,sans-serif;"><h1 style="font-weight:100;">'.$lang['notloggedin'].'</h1></body></html>');
	}else{
		global $form, $plugins;
		set('sitename', (isset($_SESSION['sitename'])) ? set('sitename', $_SESSION['sitename']) : set('sitename', SITE_NAME) );
		set('app_version', SITE_VERSION);
		set('username', $_SESSION['name']);
		set('form', json_encode($form[$_SESSION['dbprefix']]));
		set('people', search());
		set('plugins', json_encode($plugins));
		set('lang', $lang);
		return html('home.html.php');
	}
}










/**
 * search
 * Search people
 * @param (string) Pass a string to search or empty for all results
 */
function search($s='') {
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 'r')===-1) {
		$response = array('status'=>'error','message'=>"Your user cannot search contacts");
	}else{
		global $c;
		// Search multiple words
		$w = explode(' ', $s);
		foreach ($w as &$wi) {
			$wi = "(name LIKE  '%".$c->real_escape_string($wi)."%' OR form LIKE  '%".$c->real_escape_string($wi)."%' OR comments LIKE  '%".$c->real_escape_string($wi)."%') ";
		}
		$q = "SELECT `id`, `name`, `form` FROM `".$_SESSION['dbprefix']."people` WHERE ". implode(' AND ', $w) ." ORDER BY `name` ASC LIMIT 0, 50";
		$people = db($q, $c);

		foreach($people as &$person) {
			$person['form'] = json_decode($person['form'], true);
		}
//var_dump($people);
		if ($people) {
			return json($people);
		}else{
			if ($q) {
				return json(array('status'=>'warning','message'=>'No results'));
			}
		}
	}
}








/**
 * get
 * Get person details
 * @param (integer/string) Pass either person's ID or a search with just one result
 */
function get($detail) {
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 'r')===-1) {
		$response = array('status'=>'error','message'=>"Your user cannot read");
	}else{
		global $c;
		if (is_numeric($detail)) {
			$people = db("SELECT * FROM `".$_SESSION['dbprefix']."people` WHERE `id` = '".$c->real_escape_string($detail)."' LIMIT 1;", $c);
		}else{
			$people = db("SELECT * FROM `".$_SESSION['dbprefix']."people` WHERE `name` LIKE '%".$c->real_escape_string($detail)."%' OR `form` LIKE '%".$c->real_escape_string($detail)."%' ORDER BY `updated` DESC LIMIT 1;", $c);
		}
		if ($people) {
			$people = $people[0];
			$people['form'] = json_decode($people['form'], true); // decode to later encode but no other way :(
			$people['comments'] = json_decode($people['comments'], true); // decode to later encode but no other way :(
			$response = ($people);
		}else{
			$response = array('status'=>'error','message'=>'User does not exist');
		}
	}
	return json($response);
}











/**
 * save
 * Save person details
 */
function save() {
	// Check if user has enough level to Save
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 's')===-1) {
		$response = json(array('status'=>'error','message'=>'Your user cannot save'));

	// User can Save
	}else if (!isset($_POST['name']) OR $_POST['name']=='') {
		$response = array('status'=>'error','message'=>'Please write a name for the contact');
	}else{

		global $form, $c;
		$array = array(
			'title' => $_POST['title']
		);

		foreach ($form[$_SESSION['dbprefix']] as $field) {
			$array[$field['name']] = $_POST[$field['name']];
		}
//var_dump($array);
		if ($_POST['id']) { // update details
			$q = "UPDATE `".$_SESSION['dbprefix']."people` SET
				`form` = '".$c->real_escape_string(json_encode($array))."',
				`name` = '".$c->real_escape_string($_POST['name'])."',
				`updated` = '".time()."' WHERE `id` = '".$c->real_escape_string($_POST['id'])."';";
		}else{ // create new
			$q = "INSERT INTO `".$_SESSION['dbprefix']."people` VALUES (
				NULL,
				'".$c->real_escape_string($_POST['name'])."',
				'".$c->real_escape_string(json_encode($array))."',
				'{}',
				'".time()."',
				'".time()."'
			);";
		}
//var_dump($q);
		$result = db($q, $c);

		if ($result) {
			if ($_POST['id']) {
				$response = json(array('status'=>'success','message'=>'Contact details saved'));
			}else{
				// Get the ID
				$q = "SELECT `id` FROM `".$_SESSION['dbprefix']."people` ORDER BY `id` DESC LIMIT 1;";
				$id = db($q, $c);
				$response = json(array('id'=>$id[0]['id'],'status'=>'success','message'=>'New contact created'));
			}
		}else{
			$response = json(array('status'=>'error','message'=>'Could not save contact details'));
		}
	}
	return $response;
}









/**
 * delete
 * Delete person
 * @param (integer) Person ID
 */
function delete($id) {
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 'd')===-1) {
		$response = json(array('status'=>'error','message'=>"Your user cannot delete"));
	}else{
		global $c;
		$deletion = db("DELETE FROM `".$_SESSION['dbprefix']."people` WHERE `id` = '".$c->real_escape_string($id)."';", $c);

		if ($deletion) {
			$response = json(array('status'=>'success','message'=>'Contact deleted'));
		}else{
			$response = json(array('status'=>'error','message'=>'Contact could not be deleted'));
		}
	}
	return $response;
}










/********************************************************
 * COMMENTS
 *******************************************************/

/**
 * comment
 * Add a comment to the person
 * @param (integer) Person ID
 */
function comment($id) {
	global $lang;
	// Check if user has enough level to Comment
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 'c')===-1) {
		$response = json(array('status'=>'error','message'=>'Your user cannot comment'));
	}else if (!isset($_POST['comment']) OR $_POST['comment']=='') {
		json(array('status'=>'error','message'=>$lang['writecommentfirst']));
	}else{
		global $c;
		$comments = db("SELECT `comments` FROM `".$_SESSION['dbprefix']."people` WHERE id = '".$c->real_escape_string($_POST['id'])."';", $c);
		$comments = json_decode($comments[0]['comments'], true);
//var_dump($comments);
		array_unshift($comments, array(
			'id' => generateRandomString(20),
			'user' => $_SESSION['name'],
			'date' => date('c', time()), // iso 8601 format
			'text' => $_POST['comment']
		));
		$q = "UPDATE `".$_SESSION['dbprefix']."people` SET `comments` = '".$c->real_escape_string(json_encode($comments))."' WHERE `id` = '".$c->real_escape_string($_POST['id'])."';";
		$result = db($q, $c);

		if ($result) {
			$response = json($comments);
		}else{
			$response = json(array('status'=>'error','message'=>'Could not add comment'));
		}
	}
	return $response;
}


/**
 * commentdelete
 * Delete comment
 * @param (integer) Comment ID
 */
function commentdelete($id) {
	if (!isset($_SESSION['level']) OR strpos($_SESSION['level'], 'd')===-1) {
		$response = json(array('status'=>'error','message'=>"Your user cannot delete comments"));
	}else{
		global $c;
		// load comments from person
		$person = db("SELECT `id`, `comments` FROM `".$_SESSION['dbprefix']."people` WHERE `comments` LIKE '%".$c->real_escape_string($id)."%' ORDER BY `updated` DESC LIMIT 1;", $c);
		$person[0]['comments'] = json_decode($person[0]['comments'], true);
		// remove from array
		foreach($person[0]['comments'] as $key => $comment) {
			if ($comment['id']==$id) {
				unset($person[0]['comments'][$key]);
				break;
			}
		}
		// update person
		$result = db("UPDATE `".$_SESSION['dbprefix']."people` SET
			`comments` = '".$c->real_escape_string(json_encode($person[0]['comments']))."'
			WHERE `id` = '".($person[0]['id'])."';", $c);
		if ($result) {
			$response = json(array(
				'status'=>'success',
				'message'=>"Comment deleted",
				'comments'=>$person[0]['comments'])
			);
		}else{
			$response = json(array('status'=>'error','message'=>"Error deleting comment"));
		}
	}
	return $response;
}





/********************************************************
 * HELPERS
 *******************************************************/
function generateRandomString($length = 100) {
	$characters = '-_!$0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	$randomString = '';
	for ($i = 0; $i < $length; $i++) {
		$randomString .= $characters[rand(0, strlen($characters) - 1)];
	}
	return $randomString;
}





/********************************************************
 * LANG
 *******************************************************/
function initialize() {
	global $lang;
	//echo $_SESSION['lang'];
	 if (isset($_SESSION['lang'])) {
		require('lang/'.$_SESSION['lang'].'.php');
		//echo file_get_contents('lang/'.$_SESSION['lang'].'.php');
	 }else{
	 	require('lang/'.LANG_DEFAULT.'.php');
	 }
}








/********************************************************
 * LOAD PLUGINS
 *******************************************************/
foreach ($plugins as $plugin) {
	if (file_exists($plugin)) {
		require_once('plugins/'.$plugin.'/'.$plugin.'.php');
	}
}










// Let the party begin!
run();
/* =============================================================
 * bootstrap-collapse.js v2.3.1
 * http://twitter.github.com/bootstrap/javascript.html#collapse
 * =============================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */


!function(e){"use strict";var t=function(t,n){this.$element=e(t);this.options=e.extend({},e.fn.collapse.defaults,n);if(this.options.parent){this.$parent=e(this.options.parent)}this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension();n=e.camelCase(["scroll",t].join("-"));r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide");i||r.data("collapse",null)}this.$element[t](0);this.transition("addClass",e.Event("show"),"shown");e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension();this.reset(this.$element[t]());this.transition("removeClass",e.Event("hide"),"hidden");this.$element[t](0)},reset:function(e){var t=this.dimension();this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth;this.$element[e!==null?"addClass":"removeClass"]("collapse");return this},transition:function(t,n,r){var i=this,s=function(){if(n.type=="show")i.reset();i.transitioning=0;i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1;this.$element[t]("in");e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);if(!i)r.data("collapse",i=new t(this,s));if(typeof n=="string")i[n]()})};e.fn.collapse.defaults={toggle:true};e.fn.collapse.Constructor=t;e.fn.collapse.noConflict=function(){e.fn.collapse=n;return this};e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed");e(i).collapse(s)})}(window.jQuery)
/* ============================================================
 * bootstrap-dropdown.js v2.3.1
 * http://twitter.github.com/bootstrap/javascript.html#dropdowns
 * ============================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */
!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;if(!n){n=t.attr("href");n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")}r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;s=i(n);o=s.hasClass("open");r();if(!o){s.toggleClass("open")}n.focus();return false},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this);n.preventDefault();n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r);a=u.hasClass("open");if(!a||a&&n.keyCode==27){if(n.which==27)u.find(t).focus();return r.click()}s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus"));if(n.keyCode==38&&f>0)f--;if(n.keyCode==40&&f<s.length-1)f++;if(!~f)f=0;s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");if(!i)r.data("dropdown",i=new n(this));if(typeof t=="string")i[t].call(r)})};e.fn.dropdown.Constructor=n;e.fn.dropdown.noConflict=function(){e.fn.dropdown=s;return this};e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery)
/*
	,o888888o.    8 888888888o.            ,8.       ,8.          `8.`8888.      ,8'
   8888     `88.  8 8888    `88.          ,888.     ,888.          `8.`8888.    ,8'
,8 8888       `8. 8 8888     `88         .`8888.   .`8888.          `8.`8888.  ,8'
88 8888           8 8888     ,88        ,8.`8888. ,8.`8888.          `8.`8888.,8'
88 8888           8 8888.   ,88'       ,8'8.`8888,8^8.`8888.          `8.`88888'
88 8888           8 888888888P'       ,8' `8.`8888' `8.`8888.         .88.`8888.
88 8888           8 8888`8b          ,8'   `8.`88'   `8.`8888.       .8'`8.`8888.
`8 8888       .8' 8 8888 `8b.       ,8'     `8.`'     `8.`8888.     .8'  `8.`8888.
   8888     ,88'  8 8888   `8b.    ,8'       `8        `8.`8888.   .8'    `8.`8888.
	`8888888P'    8 8888     `88. ,8'         `         `8.`8888. .8'      `8.`8888.

Copyright (c) 2013 Xavi Esteve (MIT License)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

var g=[].slice;String.prototype.autoLink=function(){var e,b,d,a,c,f;c=1<=arguments.length?g.call(arguments,0):[];d="";a=c[0];f=/(^|\s)(\b(https?|ftp):\/\/[\-A-Z0-9+\u0026@#\/%?=~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~_|])/gi;if(!(0<c.length))return this.replace(f,"$1<a href='$2'>$2</a>");null!=a.callback&&"function"===typeof a.callback&&(e=a.callback,delete a.callback);for(b in a)c=a[b],d+=" "+b+"='"+c+"'";return this.replace(f,function(a,c,b){a=e&&e(b);return""+c+(a||"<a href='"+b+"'"+d+">"+b+"</a>")})};// github.com/bryanwoods/autolink-js


var crmx = {
	config: {
		sitename: '',
		username: '',
		sort: 'name',
		lang : ''
	},
	form: {},
	people: {},

	timer: false,

	// Methods to load stuff
	load: {

		/**
		 * load.form
		 * Generates the HTML form (without values)
		 * @params (object)
		 */
		form: function(form) {"use strict";
			$('#form').html('');
			//$('#searchable').html('');
			for (var i in form) {

				// TABLE HEADER
				// First add heading to the table thead
				if (!form[i].hidden) {
					$('#people-table>thead>tr').append('<th data-name="'+form[i].name+'">'+form[i].title+'</th>');
				}

				// detect field type and generate its html
				if (form[i].type==='select') {
					// FORM
					form[i].html = '<select id="'+form[i].name+'">';
					// TOP NAV
					$('.nav').append('<li class="dropdown">' +
						'<a href="#" class="dropdown-toggle" data-toggle="dropdown">'+form[i].title+' <span class="caret"></span></a>' +
						'<ul id="nav-drop-'+form[i].name+'" class="dropdown-menu">' +
							'</ul>' +
						'</li>');

						for (var j in form[i].list) {
							// Add to select
							form[i].html += '<option>'+form[i].list[j]+'</option>';

							// Add to top-nav
							$('#nav-drop-'+form[i].name).append('<li data-search=\'"'+form[i].name+'":"'+form[i].list[j]+'"\'><a href="#">'+form[i].list[j]+'</a></li>');

						}

					form[i].html += '</select>';

				}else{
					form[i].html = '';

					if (form[i].type == null) {
						form[i].type = 'text';
					}

					// Add special button shortcuts
					if (form[i].type==='email' || form[i].type==='search' || form[i].type==='tel' || form[i].type==='url' ) {
						form[i].html += '<div class="input-append">';
					}

					form[i].html += '<input type="'+form[i].type+'" id="'+form[i].name+'" placeholder="'+form[i].title+'">';

					// Add special button shortcuts
					if (form[i].type==='email') {
						form[i].html += '<a class="btn smart-link" data-smart="email" href="#" title="'+crmx.config.lang.emailcontact+'"><i class="icon-envelope"></i></a></div>';
					}else if (form[i].type==='search') {
						form[i].html += '<a class="btn smart-link" data-smart="search" href="#" title="'+crmx.config.lang.searchgoogle+'"><i class="icon-search"></i></a></div>';
					}else if (form[i].type==='tel') {
						form[i].html += '<a class="btn smart-link" data-smart="call" href="#" title="'+crmx.config.lang.callcontact+'">&phone;</a></div>';
					}else if (form[i].type==='url') {
						form[i].html += '<a class="btn smart-link" data-smart="url" href="#" title="'+crmx.config.lang.visitwebsite+'"><i class="icon-globe"></i></a></div>';
					}
				}

				// append field
				$('#form').append(
					'<div class="control-group span6">' +
						'<label class="control-label" for="'+form[i].name+'">'+form[i].title+'</label>' +
						'<div class="controls">' +
							form[i].html +
						'</div>' +
					'</div>'
				);
			}

			// Smart Links
			$('.smart-link').on('click', function() {
				switch($(this).data('smart')) {
					case 'email':
						window.open('mailto:'+$(this).siblings('input').val(), '_blank');
					break;
					case 'search':
						window.open('https://www.google.com/search?q='+$(this).siblings('input').val(), '_blank');
					break;
					case 'call':
						window.open('skype:'+$(this).siblings('input').val()+'?call', '_blank');
					break;
					case 'url':
						window.open($(this).siblings('input').val(), '_blank');
					break;
				}
				return false;
			});


		},

		/**
		 * load.people
		 * Fill in people list
		 * @params (object)
		 */
		people: function(people) {"use strict";
			$('#people-table>tbody').html('');
			if ( !people.length ) { return null; }
			crmx.sort(crmx.config.sort, people); // TODO: Optimize so it doesn't sort every time
			for (var i in people) {

				$('#people-table>tbody').append('<tr id="person-'+i+'"><td><a href="#" data-id="'+people[i].id+'"><button class="btn btn-mini hidden-phone"><i class="icon-search"></i></button> <strong>'+people[i].name+'</strong></a></td><td>'+people[i].form.title+'</td></tr>');

				var tr = $('#people-table>tbody>tr#person-'+i);
				for (var j in crmx.form) {
					if (!crmx.form[j].hidden) {
						tr.append('<td>'+((people[i].form[crmx.form[j].name]) ? people[i].form[crmx.form[j].name] : '')+'</td>');
					}
				}
			}
		},

		/**
		 * load.person
		 * Fills the values in name, title and form fields
		 * @params (object)
		 */
		// person: function(person) {"use strict";
		// 	// TODO
		// 	$('#commentbox').fadeIn();
		// 	for(var i in crmx.form) {
		// 		$('#'+crmx.form[i].name).val( person[crmx.form[i].name] );
		// 	}
		// },

		/**
		 * load.comments
		 * Fills the comments for that person
		 * @params (object)
		 */
		comments: function(comments) {"use strict";
			$('#comments').html('');
			for(var i in comments) {
				$('#comments').append('<blockquote>'+comments[i].text.autoLink()+'<small><em class="easydate">'+comments[i].date+'</em> '+crmx.config.lang.by+' <strong>'+comments[i].user+'</strong> <a href="#" class="comment-delete" data-id="'+comments[i].id+'" title="'+crmx.config.lang.deletecomment+'">&times;</a></small></blockquote>');
			}
			$(".easydate").easydate();
		}
	},// load


	/**
	 * refresh
	 * Asks the server for an updated list of people
	 */
	refresh: function() {"use strict";
		crmx.search( $('#s').val() );
	},

	/**
	 * save
	 * Saves or Creates a person details
	 */
	save: function(){"use strict";
		crmx.notification(crmx.config.lang.loading);
		var data = {
			id: $('#id').val(),
			name: $('#name').val(),
			title: $('#title').val()
		};

		for(var i in crmx.form) {
			data[crmx.form[i].name] = $('#'+crmx.form[i].name).val();
		}

		$.ajax({
			type: "POST",
			url: "save",
			data: data
		}).done(function( response ) {
			crmx.notification( response.message, response.status );
			if (response.status==='success') {
				crmx.notification(crmx.config.lang.saved, 'success');
				$('.save').fadeOut();
				$('#delete').fadeIn();
				$('#commentbox').fadeIn();
				crmx.refresh();
				// If created a new person then select him
				if (response.id) {
					$('#id').val(response.id);
					$('#people li a[data-id='+response.id+']').parent().addClass('active');
				}
			}
		});
	},

	/**
	 * remove
	 * Deletes a person from the database
	 * @params (integer) ID of the person
	 */
	remove: function(id) {"use strict";
		if (!id) {return false;}
		if (confirm(crmx.config.lang.contactdeleteconfirm)===true) {
			crmx.notification('Deleting&hellip;');
			$.ajax({
				type: "DELETE",
				url: "delete/"+id
			}).done(function( response ) {
				crmx.notification( response.message, response.status );
				if (response.status==='success') {
					// Clean up!
					$('#name').val('');
					$('#title').val('');
					for(var i in crmx.form) {
						$('#'+crmx.form[i].name).val('');
					}
					$('.save').fadeOut();
					$('#delete').fadeOut();
					$('#commentbox').fadeOut();
					crmx.search('');
				}else{
					crmx.notification( response.message, response.status );
				}
			});
		}
	},

	/**
	 * comment
	 * Add a comment to the person
	 * @params (integer) ID of the person
	 * @params (string) Comment
	 */
	comment: function(id, comment) {"use strict";
		crmx.notification(''+crmx.config.lang.commenting+'&hellip;');
		$.ajax({
			type: "POST",
			url: "comment",
			data: {id: id, comment: comment}
		}).done(function( response ) {
			if (response.status!=='error') {
				crmx.notification();
				$('#c').val('');
				crmx.load.comments(response);
			}else{
				crmx.notification( response.message, response.status );
			}
		});
	},

	/**
	 * get
	 * Gets a person's information
	 * @params (integer) ID of the person
	 */
	get: function(id) {"use strict";
		crmx.notification(crmx.config.lang.loading+'&hellip;');
		$.ajax({
			type: "GET",
			url: "get/"+id
		}).done(function( response ) {
			if (response.status!=='error') {
				crmx.notification();

				$('#name').val(response.name);
				$('#id').val(response.id);
				$('#title').val(response.form.title);
				for(var i in crmx.form) {
					$('#'+crmx.form[i].name).val( response.form[crmx.form[i].name] );
				}

				$('.save').fadeOut();
				$('#delete').fadeIn();
				$('#commentbox').fadeIn();

				$('#people li').removeClass('active');
				$('#people li a[data-id='+id+']').parent().addClass('active');

				crmx.load.comments(response.comments);

				document.location = '#main';

			}else{
				crmx.notification( response.message, response.status );
			}
		});
	},

	/**
	 * search
	 * Searches in the database
	 * @params (string) Search query
	 */
	search: function(query) {"use strict";
		$('#people').prepend('<li class="nav-header">'+crmx.config.lang.searching+'&hellip;</li>');
		$.ajax({
			type: "GET",
			url: "search/"+query
		}).done(function( response ) {
			if (!response.status) {
				crmx.load.people(response);
			}else{
				crmx.notification( response.message, response.status );
				crmx.load.people({});
			}
		});
	},

	/**
	 * notification
	 * Shows a notification
	 * @params (string) Message or empty to hide notification
	 * @params (string) Can be ok, error or info
	 */
	notification: function(message, type) {"use strict";
		if (message==null) {
			$('#notification').stop().fadeOut('fast');
		}else{
			$('#notification')
				.html( ((type)?'<strong>'+type+'</strong> ':'')+message)
				.attr('class', 'alert alert-'+type)
				.fadeIn(500, function(){
					$(this).stop().fadeOut(10000);
			});
		}
	},


	/**
	 * updateui
	 * Shows or hides buttons to unclutter UI
	 */
	updateui: function() {"use strict";
		if ($('#id').val().length>0) {
			$('.save').fadeIn().html(crmx.config.lang.contactsave);
			$('#delete').fadeIn();
		}else{
			$('.save').fadeIn().html(crmx.config.lang.contactnew);
			$('#delete').fadeOut();
		}
	},


	/**
	 * clearform
	 *
	 */
	clearform: function() {"use strict";
		$('#main input,#main select').val('');
		$('#delete').addClass('hide');
		$('#comments').empty();
		crmx.updateui();
		$('#name').focus();
	},


	/**
	 * sort
	 *
	 */
	sort: function(column, data) {"use strict";
		if ( !data.length ) { return null; }
		function createSorter(column) {
			return function (a,b) {
				if (column==='name') {
					var aVal = a[column], bVal = b[column];
				}else{
				 	var aVal = a.form[column], bVal = b.form[column];
				}

				if(typeof aVal === 'string')
					aVal = aVal.toLowerCase();
				if(typeof bVal === 'string')
					bVal = bVal.toLowerCase();

				return aVal > bVal ? 1 : (aVal < bVal ? - 1 : 0);
			};
		}
		return data.sort(createSorter(column));
	},


	/**
	 * run
	 * Starts the app, loads people and form, binds events
	 */
	run: function() {"use strict";

		crmx.load.people(crmx.people);
		crmx.load.form(crmx.form);

		// Load plugins' JS and CSS
		for (var i = 0; i < crmx.config.plugins.length; i++) {
			var plugin = crmx.config.plugins[i];
			// CSS
			$.ajax({
				url: 'plugins/'+plugin+'/'+plugin+'.css',
				type:'HEAD',
				success: function() {
					$('head').append('<link href="plugins/'+plugin+'/'+plugin+'.css" rel="stylesheet">');
				}
			});
			// JS
			$.ajax({
				url: 'plugins/'+plugin+'/'+plugin+'.js',
				type:'HEAD',
				success: function() {
					$('body').append('<script src="plugins/'+plugin+'/'+plugin+'.js"></script>');
				}
			});
		}


		/********************************************
		 * Event Binding
		 ********************************************/
		// Save person
		$('.save').on('click', function(){
			crmx.save();
			return false;
		});
		// Delete person
		$('#delete').on('click', function(){
			crmx.remove( $('#id').val() );
			return false;
		});

		// Update UI (can be optimised)
		$('#form').on('change', 'select', function() {crmx.updateui();});
		$('#main').on('input paste', 'input', function() {crmx.updateui();});
		$('#name').on('input paste', function() {crmx.updateui();});
		$('#title').on('input paste', function() {crmx.updateui();});

		// Search box
		$('#s').on('input paste', function() {
			if (crmx.timer===false) {
				crmx.timer = true;
				var t = setTimeout(function(){ crmx.search($('#s').val());crmx.timer=false; }, 500);
			}
		}).on('keypress', function(e) {
			if(e.keyCode == 13) return false;
		});

		// Add comment
		$('#c_button').on('click', function(){
			if ($('#id').val().length<1) {
				crmx.notification(crmx.config.lang.selectnamefirst, 'error');
			}
			if ($('#c').val().length<1) {
				crmx.notification(crmx.config.lang.entercommentfirst, 'error');
			}
			crmx.comment($('#id').val(), $('#c').val().replace(/\n/g, '<br>'));
			return false;
		});

		// Delete comment
		$('#comments').on('click', '.comment-delete', function() {
			if(confirm(crmx.config.lang.confirmdeletecomment)) {
				$.ajax({
					type: "DELETE",
					url: "comment/"+$(this).data('id')
				}).done(function( response ) {
					crmx.notification(response.message, response.status);
					if (response.status==='success') {
						crmx.load.comments(response.comments);
					}
				});
			}
			return false;
		});

		// Top nav dropdown filters
		$('.dropdown-menu').click(function(e){
			var target = $(e.target).closest('li'); // the child that fired the original click
			crmx.search(target.data('search'));
			$('#s').val(target.data('search'));
		});

		// Table person click
		$('#people-table').on('click', 'a', function(){
			crmx.get( $(this).data('id') );
			return false;
		});

		// Table sort by column
		$('#people-table thead').on('click', 'th', function(){
			$('#people-table th').removeClass('active');
			$(this).addClass('active');
			crmx.config.sort = $(this).data('name');
			crmx.load.people( crmx.people );
		});


		// Clear form (add new)
		$('.clearform').click(function(){crmx.clearform();});
		$('.refresh').click(function(){crmx.refresh();});

		// Notification click to hide
		$('#notification').on('click', function(){
			$(this).stop().fadeOut(200);
		});


		$('#s').focus();
	}


}; // crmx


/*
 * jQuery EasyDate 0.2.4 ($Rev: 54 $)
 * Copyright (c) 2009 Parsha Pourkhomami (parshap@gmail.com)
 * Licensed under the MIT license.
 */
(function(e){e.easydate={};e.easydate.locales={};e.easydate.locales.enUS={future_format:"%s %t",past_format:"%t %s",second:"second",seconds:"seconds",minute:"minute",minutes:"minutes",hour:"hour",hours:"hours",day:"day",days:"days",week:"week",weeks:"weeks",month:"month",months:"months",year:"year",years:"years",yesterday:"yesterday",tomorrow:"tomorrow",now:"just now",ago:"ago","in":"in"};var d={live:true,set_title:true,format_future:true,format_past:true,units:[{name:"now",limit:5},{name:"second",limit:60,in_seconds:1},{name:"minute",limit:3600,in_seconds:60},{name:"hour",limit:86400,in_seconds:3600},{name:"yesterday",limit:172800,past_only:true},{name:"tomorrow",limit:172800,future_only:true},{name:"day",limit:604800,in_seconds:86400},{name:"week",limit:2629743,in_seconds:604800},{name:"month",limit:31556926,in_seconds:2629743},{name:"year",limit:Infinity,in_seconds:31556926}],uneasy_format:function(p){return p.toLocaleDateString()},locale:e.easydate.locales.enUS};var f=0;var k={};var h={};var a={};function l(r,q,p){if(!isNaN(q)&&q!=1){r=r+"s"}return p.locale[r]||r}var o=e.easydate.pause=function(q){var s=function(p){clearTimeout(k[p]);delete k[p];h[p]=true};if(!q){for(var r in k){s(r)}}else{e(q).each(function(){var p=jQuery.data(this);if(!isNaN(k[p])){s(p)}})}};var c=e.easydate.resume=function(p){var s=function(r){delete h[r];b(a[r])};if(!p){for(var q in h){s(q)}}else{e(p).each(function(){var r=jQuery.data(this);if(!isNaN(h[r])){s(r)}})}};var g=e.easydate.set_now=function(q){var r;if(q instanceof Date){r=q.getTime()}else{r=Date.parse(q)}if(isNaN(r)){return}f=r-(new Date()).getTime();for(var p in a){if(!isNaN[k[p]]){clearTimeout(k[p])}b(a[p])}};var n=e.easydate.get_now=function(){var p=new Date();p.setTime(p.getTime()+f);return p};var j=e.easydate.format_date=function(q,x){var r=e.extend({},d,x);var v=((n().getTime()-q.getTime())/1000);var u=Math.abs(v);if(isNaN(v)){return}if((!r.format_future&&v<0)||(!r.format_past&&v>0)){return}for(var s in r.units){var w=r.units[s];if((w.past_only&&v<0)||(w.future_only&&v>0)){continue}if(u<w.limit){if(isNaN(w.in_seconds)){return l(w.name,NaN,r)}var p=u/w.in_seconds;p=Math.round(p);var t;if(v<0){t=l("future_format",NaN,r).replace("%s",l("in",NaN,r))}else{t=l("past_format",NaN,r).replace("%s",l("ago",NaN,r))}return t.replace("%t",p+" "+l(w.name,p,r))}}return r.uneasy_format(q)};function m(r,t){var q=n();var w=((q.getTime()-r.getTime())/1000);var p=Math.abs(w);if(isNaN(w)){return}var v=0;for(var s in t.units){var u=t.units[s];if((u.past_only&&w<0)||(u.future_only&&w>0)){continue}if(p<u.limit){if(isNaN(u.in_seconds)){if(w<0){return(v-p)*1000+100}else{return(u.limit-p)*1000+100}}else{if(w<0){return(p%u.in_seconds)*1000+100}else{return(u.in_seconds-(p%u.in_seconds))*1000+100}}}v=u.limit}if(w<0){for(var s=t.units.length-1;s>=0;s--){var u=t.units[s];if(u.past_only){continue}return(u.limit-p)*1000+100}}}function i(q,r){var p=q.data("easydate.date");if(isNaN(p)){var s;var t=Date.parse(s=q.attr("title"))||Date.parse(s=q.html());if(!isNaN(t)){p=new Date();p.setTime(t);q.data("easydate.date",p);if(r.set_title&&!q.attr("title")){q.attr("title",s)}}}return p}function b(r){var s=r.data("easydate.settings");var p=e.data(r[0]);a[p]=r;delete k[p];var q=i(r,s);if(isNaN(q)){return}r.html(j(q,s));if(s.live){var t=m(q,s);if(!isNaN(t)){if(t>2147483647){t=2147483647}var u=setTimeout(function(){b(r)},Math.round(t));k[p]=u}}}e.fn.easydate=function(p){var q=e.extend({},d,p);this.data("easydate.settings",q);this.removeData("easydate.date");this.each(function(){var r=e.data(this);if(!isNaN(k[r])){clearTimeout(k[r]);delete k[r]}b(e(this))})}})(jQuery);
/* =============================================================
 * bootstrap-collapse.js v2.3.1
 * http://twitter.github.com/bootstrap/javascript.html#collapse
 * =============================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */!function(e){"use strict";var t=function(t,n){this.$element=e(t);this.options=e.extend({},e.fn.collapse.defaults,n);this.options.parent&&(this.$parent=e(this.options.parent));this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension();n=e.camelCase(["scroll",t].join("-"));r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide");i||r.data("collapse",null)}this.$element[t](0);this.transition("addClass",e.Event("show"),"shown");e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension();this.reset(this.$element[t]());this.transition("removeClass",e.Event("hide"),"hidden");this.$element[t](0)},reset:function(e){var t=this.dimension();this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth;this.$element[e!==null?"addClass":"removeClass"]("collapse");return this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset();i.transitioning=0;i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1;this.$element[t]("in");e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s));typeof n=="string"&&i[n]()})};e.fn.collapse.defaults={toggle:!0};e.fn.collapse.Constructor=t;e.fn.collapse.noConflict=function(){e.fn.collapse=n;return this};e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed");e(i).collapse(s)})}(window.jQuery);!function(e){"use strict";function t(){e(r).each(function(){n(e(this)).removeClass("open")})}function n(t){var n=t.attr("data-target"),r;if(!n){n=t.attr("href");n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")}r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var r="[data-toggle=dropdown]",i=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};i.prototype={constructor:i,toggle:function(r){var i=e(this),s,o;if(i.is(".disabled, :disabled"))return;s=n(i);o=s.hasClass("open");t();o||s.toggleClass("open");i.focus();return!1},keydown:function(t){var i,s,o,u,a,f;if(!/(38|40|27)/.test(t.keyCode))return;i=e(this);t.preventDefault();t.stopPropagation();if(i.is(".disabled, :disabled"))return;u=n(i);a=u.hasClass("open");if(!a||a&&t.keyCode==27){t.which==27&&u.find(r).focus();return i.click()}s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus"));t.keyCode==38&&f>0&&f--;t.keyCode==40&&f<s.length-1&&f++;~f||(f=0);s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var n=e(this),r=n.data("dropdown");r||n.data("dropdown",r=new i(this));typeof t=="string"&&r[t].call(n)})};e.fn.dropdown.Constructor=i;e.fn.dropdown.noConflict=function(){e.fn.dropdown=s;return this};e(document).on("click.dropdown.data-api",t).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api",r,i.prototype.toggle).on("keydown.dropdown.data-api",r+", [role=menu]",i.prototype.keydown)}(window.jQuery)(function(e){function o(e,t,n){!isNaN(t)&&t!=1&&(e+="s");return n.locale[e]||e}function h(e,t){var n=l(),r=(n.getTime()-e.getTime())/1e3,i=Math.abs(r);if(isNaN(r))return;var s=0;for(var o in t.units){var u=t.units[o];if(u.past_only&&r<0||u.future_only&&r>0)continue;if(i<u.limit)return isNaN(u.in_seconds)?r<0?(s-i)*1e3+100:(u.limit-i)*1e3+100:r<0?i%u.in_seconds*1e3+100:(u.in_seconds-i%u.in_seconds)*1e3+100;s=u.limit}if(r<0)for(var o=t.units.length-1;o>=0;o--){var u=t.units[o];if(u.past_only)continue;return(u.limit-i)*1e3+100}}function p(e,t){var n=e.data("easydate.date");if(isNaN(n)){var r,i=Date.parse(r=e.attr("title"))||Date.parse(r=e.html());if(!isNaN(i)){n=new Date;n.setTime(i);e.data("easydate.date",n);t.set_title&&!e.attr("title")&&e.attr("title",r)}}return n}function d(t){var n=t.data("easydate.settings"),i=e.data(t[0]);s[i]=t;delete r[i];var o=p(t,n);if(isNaN(o))return;t.html(c(o,n));if(n.live){var u=h(o,n);if(!isNaN(u)){u>2147483647&&(u=2147483647);var a=setTimeout(function(){d(t)},Math.round(u));r[i]=a}}}e.easydate={};e.easydate.locales={};e.easydate.locales.enUS={future_format:"%s %t",past_format:"%t %s",second:"second",seconds:"seconds",minute:"minute",minutes:"minutes",hour:"hour",hours:"hours",day:"day",days:"days",week:"week",weeks:"weeks",month:"month",months:"months",year:"year",years:"years",yesterday:"yesterday",tomorrow:"tomorrow",now:"just now",ago:"ago","in":"in"};var t={live:!0,set_title:!0,format_future:!0,format_past:!0,units:[{name:"now",limit:5},{name:"second",limit:60,in_seconds:1},{name:"minute",limit:3600,in_seconds:60},{name:"hour",limit:86400,in_seconds:3600},{name:"yesterday",limit:172800,past_only:!0},{name:"tomorrow",limit:172800,future_only:!0},{name:"day",limit:604800,in_seconds:86400},{name:"week",limit:2629743,in_seconds:604800},{name:"month",limit:31556926,in_seconds:2629743},{name:"year",limit:Infinity,in_seconds:31556926}],uneasy_format:function(e){return e.toLocaleDateString()},locale:e.easydate.locales.enUS},n=0,r={},i={},s={},u=e.easydate.pause=function(t){var n=function(e){clearTimeout(r[e]);delete r[e];i[e]=!0};if(!t)for(var s in r)n(s);else e(t).each(function(){var e=jQuery.data(this);isNaN(r[e])||n(e)})},a=e.easydate.resume=function(t){var n=function(e){delete i[e];d(s[e])};if(!t)for(var r in i)n(r);else e(t).each(function(){var e=jQuery.data(this);isNaN(i[e])||n(e)})},f=e.easydate.set_now=function(e){var t;e instanceof Date?t=e.getTime():t=Date.parse(e);if(isNaN(t))return;n=t-(new Date).getTime();for(var i in s){isNaN[r[i]]||clearTimeout(r[i]);d(s[i])}},l=e.easydate.get_now=function(){var e=new Date;e.setTime(e.getTime()+n);return e},c=e.easydate.format_date=function(n,r){var i=e.extend({},t,r),s=(l().getTime()-n.getTime())/1e3,u=Math.abs(s);if(isNaN(s))return;if(!i.format_future&&s<0||!i.format_past&&s>0)return;for(var a in i.units){var f=i.units[a];if(f.past_only&&s<0||f.future_only&&s>0)continue;if(u<f.limit){if(isNaN(f.in_seconds))return o(f.name,NaN,i);var c=u/f.in_seconds;c=Math.round(c);var h;s<0?h=o("future_format",NaN,i).replace("%s",o("in",NaN,i)):h=o("past_format",NaN,i).replace("%s",o("ago",NaN,i));return h.replace("%t",c+" "+o(f.name,c,i))}}return i.uneasy_format(n)};e.fn.easydate=function(n){var i=e.extend({},t,n);this.data("easydate.settings",i);this.removeData("easydate.date");this.each(function(){var t=e.data(this);if(!isNaN(r[t])){clearTimeout(r[t]);delete r[t]}d(e(this))})}})(jQuery);var g=[].slice;String.prototype.autoLink=function(){var e,t,n,r,i,s;i=1<=arguments.length?g.call(arguments,0):[];n="";r=i[0];s=/(^|\s)(\b(https?|ftp):\/\/[\-A-Z0-9+\u0026@#\/%?=~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~_|])/gi;if(0<i.length){null!=r.callback&&"function"==typeof r.callback&&(e=r.callback,delete r.callback);for(t in r)i=r[t],n+=" "+t+"='"+i+"'";return this.replace(s,function(t,r,i){t=e&&e(i);return""+r+(t||"<a href='"+i+"'"+n+">"+i+"</a>")})}return this.replace(s,"$1<a href='$2'>$2</a>")};var crmx={config:{sitename:"",username:"",sort:"name",lang:""},form:{},people:{},timer:!1,load:{form:function(e){"use strict";$("#form").html("");for(var t in e){e[t].hidden||$("#people-table>thead>tr").append('<th data-name="'+e[t].name+'">'+e[t].title+"</th>");if(e[t].type==="select"){e[t].html='<select id="'+e[t].name+'">';$(".nav").append('<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">'+e[t].title+' <span class="caret"></span></a>'+'<ul id="nav-drop-'+e[t].name+'" class="dropdown-menu">'+"</ul>"+"</li>");for(var n in e[t].list){e[t].html+="<option>"+e[t].list[n]+"</option>";$("#nav-drop-"+e[t].name).append("<li data-search='\""+e[t].name+'":"'+e[t].list[n]+'"\'><a href="#">'+e[t].list[n]+"</a></li>")}e[t].html+="</select>"}else{e[t].html="";e[t].type==null&&(e[t].type="text");if(e[t].type==="email"||e[t].type==="search"||e[t].type==="tel"||e[t].type==="url")e[t].html+='<div class="input-append">';e[t].html+='<input type="'+e[t].type+'" id="'+e[t].name+'" placeholder="'+e[t].title+'">';e[t].type==="email"?e[t].html+='<a class="btn smart-link" data-smart="email" href="#" title="'+crmx.config.lang.emailcontact+'"><i class="icon-envelope"></i></a></div>':e[t].type==="search"?e[t].html+='<a class="btn smart-link" data-smart="search" href="#" title="'+crmx.config.lang.searchgoogle+'"><i class="icon-search"></i></a></div>':e[t].type==="tel"?e[t].html+='<a class="btn smart-link" data-smart="call" href="#" title="'+crmx.config.lang.callcontact+'"><i class="icon-bullhorn"></i></a></div>':e[t].type==="url"&&(e[t].html+='<a class="btn smart-link" data-smart="url" href="#" title="'+crmx.config.lang.visitwebsite+'"><i class="icon-globe"></i></a></div>')}$("#form").append('<div class="control-group span6"><label class="control-label" for="'+e[t].name+'">'+e[t].title+"</label>"+'<div class="controls">'+e[t].html+"</div>"+"</div>")}$(".smart-link").on("click",function(){switch($(this).data("smart")){case"email":window.open("mailto:"+$(this).siblings("input").val(),"_blank");break;case"search":window.open("https://www.google.com/search?q="+$(this).siblings("input").val(),"_blank");break;case"call":window.open("skype:"+$(this).siblings("input").val()+"?call","_blank");break;case"url":window.open($(this).siblings("input").val(),"_blank")}return!1})},people:function(e){"use strict";$("#people-table>tbody").html("");crmx.sort(crmx.config.sort,e);for(var t in e){$("#people-table>tbody").append('<tr id="person-'+t+'"><td><a href="#" data-id="'+e[t].id+'"><button class="btn btn-mini hidden-phone"><i class="icon-search"></i></button> <strong>'+e[t].name+"</strong></a></td><td>"+e[t].form.title+"</td></tr>");var n=$("#people-table>tbody>tr#person-"+t);for(var r in crmx.form)crmx.form[r].hidden||n.append("<td>"+(e[t].form[crmx.form[r].name]?e[t].form[crmx.form[r].name]:"")+"</td>")}},comments:function(e){"use strict";$("#comments").html("");for(var t in e)$("#comments").append("<blockquote>"+e[t].text.autoLink()+'<small><em class="easydate">'+e[t].date+"</em> "+crmx.config.lang.by+" <strong>"+e[t].user+'</strong> <a href="#" class="comment-delete" data-id="'+e[t].id+'" title="'+crmx.config.lang.deletecomment+'">&times;</a></small></blockquote>');$(".easydate").easydate()}},refresh:function(){"use strict";$("#s").val("");crmx.search("")},save:function(){"use strict";crmx.notification(crmx.config.lang.loading);var e={id:$("#id").val(),name:$("#name").val(),title:$("#title").val()};for(var t in crmx.form)e[crmx.form[t].name]=$("#"+crmx.form[t].name).val();$.ajax({type:"POST",url:"save",data:e}).done(function(e){crmx.notification(e.message,e.status);if(e.status==="success"){crmx.notification(crmx.config.lang.saved,"success");$(".save").fadeOut();$("#delete").fadeIn();$("#commentbox").fadeIn();crmx.refresh();if(e.id){$("#id").val(e.id);$("#people li a[data-id="+e.id+"]").parent().addClass("active")}}})},remove:function(e){"use strict";if(!e)return!1;if(confirm(crmx.config.lang.contactdeleteconfirm)===!0){crmx.notification("Deleting&hellip;");$.ajax({type:"DELETE",url:"delete/"+e}).done(function(e){crmx.notification(e.message,e.status);if(e.status==="success"){$("#name").val("");$("#title").val("");for(var t in crmx.form)$("#"+crmx.form[t].name).val("");$(".save").fadeOut();$("#delete").fadeOut();$("#commentbox").fadeOut();crmx.search("")}else crmx.notification(e.message,e.status)})}},comment:function(e,t){"use strict";crmx.notification(""+crmx.config.lang.commenting+"&hellip;");$.ajax({type:"POST",url:"comment",data:{id:e,comment:t}}).done(function(e){if(e.status!=="error"){crmx.notification();$("#c").val("");crmx.load.comments(e)}else crmx.notification(e.message,e.status)})},get:function(e){"use strict";crmx.notification(crmx.config.lang.loading+"&hellip;");$.ajax({type:"GET",url:"get/"+e}).done(function(t){if(t.status!=="error"){crmx.notification();$("#name").val(t.name);$("#id").val(t.id);$("#title").val(t.form.title);for(var n in crmx.form)$("#"+crmx.form[n].name).val(t.form[crmx.form[n].name]);$(".save").fadeOut();$("#delete").fadeIn();$("#commentbox").fadeIn();$("#people li").removeClass("active");$("#people li a[data-id="+e+"]").parent().addClass("active");crmx.load.comments(t.comments);document.location="#main"}else crmx.notification(t.message,t.status)})},search:function(e){"use strict";$("#people").prepend('<li class="nav-header">'+crmx.config.lang.searching+"&hellip;</li>");$.ajax({type:"GET",url:"search/"+e}).done(function(e){if(!e.status)crmx.load.people(e);else{crmx.notification(e.message,e.status);crmx.load.people({})}})},notification:function(e,t){"use strict";e==null?$("#notification").stop().fadeOut("fast"):$("#notification").html((t?"<strong>"+t+"</strong> ":"")+e).attr("class","alert alert-"+t).fadeIn(500,function(){$(this).stop().fadeOut(1e4)})},updateui:function(){"use strict";if($("#id").val().length>0){$(".save").fadeIn().html(crmx.config.lang.contactsave);$("#delete").fadeIn()}else{$(".save").fadeIn().html(crmx.config.lang.contactnew);$("#delete").fadeOut()}},clearform:function(){"use strict";$("#main input,#main select").val("");$("#delete").addClass("hide");crmx.updateui();$("#name").focus()},sort:function(e,t){"use strict";function n(e){return function(t,n){if(e==="name")var r=t[e],i=n[e];else var r=t.form[e],i=n.form[e];typeof r=="string"&&(r=r.toLowerCase());typeof i=="string"&&(i=i.toLowerCase());return r>i?1:r<i?-1:0}}return t.sort(n(e))},run:function(){"use strict";crmx.load.people(crmx.people);crmx.load.form(crmx.form);for(var e=0;e<crmx.config.plugins.length;e++){var t=crmx.config.plugins[e];$.ajax({url:"plugins/"+t+"/"+t+".css",type:"HEAD",success:function(){$("head").append('<link href="plugins/'+t+"/"+t+'.css" rel="stylesheet">')}});$.ajax({url:"plugins/"+t+"/"+t+".js",type:"HEAD",success:function(){$("body").append('<script src="plugins/'+t+"/"+t+'.js"></script>')}})}$(".save").on("click",function(){crmx.save();return!1});$("#delete").on("click",function(){crmx.remove($("#id").val());return!1});$("#form").on("change","select",function(){crmx.updateui()});$("#main").on("input paste","input",function(){crmx.updateui()});$("#name").on("input paste",function(){crmx.updateui()});$("#title").on("input paste",function(){crmx.updateui()});$("#s").on("input paste",function(){if(crmx.timer===!1){crmx.timer=!0;var e=setTimeout(function(){crmx.search($("#s").val());crmx.timer=!1},500)}}).on("keypress",function(e){if(e.keyCode==13)return!1});$("#c_button").on("click",function(){$("#id").val().length<1&&crmx.notification(crmx.config.lang.selectnamefirst,"error");$("#c").val().length<1&&crmx.notification(crmx.config.lang.entercommentfirst,"error");crmx.comment($("#id").val(),$("#c").val().replace(/\n/g,"<br>"));return!1});$("#comments").on("click",".comment-delete",function(){confirm(crmx.config.lang.confirmdeletecomment)&&$.ajax({type:"DELETE",url:"comment/"+$(this).data("id")}).done(function(e){crmx.notification(e.message,e.status);e.status==="success"&&crmx.load.comments(e.comments)});return!1});$(".dropdown-menu").click(function(e){var t=$(e.target).closest("li");crmx.search(t.data("search"));$("#s").val(t.data("search"))});$("#people-table").on("click","a",function(){crmx.get($(this).data("id"));return!1});$("#people-table thead").on("click","th",function(){$("#people-table th").removeClass("active");$(this).addClass("active");crmx.config.sort=$(this).data("name");crmx.load.people(crmx.people)});$(".clearform").click(function(){crmx.clearform()});$(".refresh").click(function(){crmx.refresh()});$("#notification").on("click",function(){$(this).stop().fadeOut(200)});$("#s").focus()}};
<?php

$lang = array(

	'lang' => 'en_us',

	// Form
	'name' => 'Name',
	'title' => 'Title',
	'emailcontact' => 'Email contact',
	'searchgoogle' => 'Search in Google',
	'callcontact' => 'Call contact',
	'visitwebsite' => 'Visit website',

	'loading' => 'Loading',
	'saved' => 'Saved',
	'by' => 'by',
	'selectnamefirst' => 'Select a name first',

	// General status messages
	'actionnotrecognized' => 'Action not recognized',

	// User
	'authfailed' => 'Authentication failed',
	'notloggedin' => 'You are not logged in',
	'userdoesntexist' => 'User does not exist',

	// Permissions
	'cantsearch' => 'Your user cannot search contacts',
	'cantcomment' => 'Your user cannot comment',
	'cantdelete' => 'Your user cannot delete',
	'cantread' => 'Your user cannot read',
	'cantsave' => 'Your user cannot save',
	'cantdeletecomments' => 'Your user cannot delete comments',

	// Search
	'searchplaceholder' => 'Start typing to search',
	'searching' => 'Searching',
	'noresults' => 'No results',

	// Contact
	'contactnew' => 'Create new',
	'contactsave' => 'Save contact',
	'contactdelete' => 'Delete contact',
	'contactcreate' => 'Add new contact',
	'contactnamerequired' => 'Please write a name for the contact',
	'contactcreated' => 'New contact created',
	'contactsaved' => 'Contact details saved',
	'contactsaveerror' => 'Could not save contact details',
	'contactdeleteconfirm' => 'Are you sure you want to delete this contact?',
	'contactdeleted' => 'Contact deleted',
	'contactdeleteerror' => 'Contact could not be deleted',

	// Layout
	'backtotop' => 'Back to top',
	'clicktoclose' => 'Click to close',
	'showall' => 'Show all',
	'viewtable' => 'View table',
	'viewdetails' => 'View details',
	'loggedinas' => 'Logged in as',
	'logout' => 'Logout',

	// Comments
	'comments' => 'Comments',
	'commenting' => 'Commenting',
	'entercommentfirst' => 'Enter a comment first',
	'confirmdeletecomment' => 'Are you sure you want to delete this comment?',
	'commentadd' => 'Add comment',
	'commentdelete' => 'Delete comment',
	'commentdeleteerror' => 'Error deleting comment',
	'commentadderror' => 'Could not add comment',
	'commentdeleted' => 'Comment deleted',

	'' => '',

);
<?php

$lang = array(

	'lang' => 'es_es',

	// Form
	'name' => 'Nombre',
	'title' => 'Título',
	'emailcontact' => 'Enviar correo electrónico',
	'searchgoogle' => 'Buscar en Google',
	'callcontact' => 'Llamar contacto',
	'visitwebsite' => 'Visitar página web',

	'loading' => 'Cargando',
	'saved' => 'Guardado',
	'by' => 'por',
	'selectnamefirst' => 'Selecciona un contacto',

	// General status messages
	'actionnotrecognized' => 'Acción no reconocida',

	// User
	'authfailed' => 'Autorización fallida',
	'notloggedin' => 'No estás conectado',
	'userdoesntexist' => 'El usuario no existe',

	// Permissions
	'cantsearch' => 'Privilegios insuficientes para buscar',
	'cantcomment' => 'Privilegios insuficientes para comentar',
	'cantdelete' => 'Privilegios insuficientes para borrar',
	'cantread' => 'Privilegios insuficientes para leer',
	'cantsave' => 'Privilegios insuficientes para editar',
	'cantdeletecomments' => 'Privilegios insuficientes para borrar comentarios',

	// Search
	'searchplaceholder' => 'Escribe aquí para buscar',
	'searching' => 'Buscando',
	'noresults' => 'No hay resultados',

	// Contact
	'contactnew' => 'Crear nuevo',
	'contactsave' => 'Guardar contacto',
	'contactdelete' => 'Borrar contacto',
	'contactcreate' => 'Añadir contacto',
	'contactnamerequired' => 'Por favor, escribe un nombre para el contacto',
	'contactcreated' => 'Nuevo contacto creado',
	'contactsaved' => 'Contacto guardado',
	'contactsaveerror' => 'Error al guardar el contacto',
	'contactdeleteconfirm' => '¿Estás seguro de que deseas borrar este contacto?',
	'contactdeleted' => 'Contacto borrado',
	'contactdeleteerror' => 'Error al borrar el contacto',

	// Layout
	'backtotop' => 'Volver arriba',
	'clicktoclose' => 'Click para cerrar',
	'showall' => 'Mostrar todos',
	'viewtable' => 'Ver tabla',
	'viewdetails' => 'Ver detalles',
	'loggedinas' => 'Conectado como',
	'logout' => 'Desconectar',

	// Comments
	'comments' => 'Comentarios',
	'commenting' => 'Comentando',
	'entercommentfirst' => 'Escribe un comentario',
	'confirmdeletecomment' => '¿Estás seguro de que deseas borrar este comentario?',
	'commentadd' => 'Añadir comentario',
	'commentdelete' => 'Borrar comentario',
	'commentdeleteerror' => 'Error al borrar el comentario',
	'commentadderror' => 'Error al añadir el comentario',
	'commentdeleted' => 'Comentario borrado',

	'' => '',

);
<?php
                                                                  
# ============================================================================ #

/**
 *  L I M O N A D E
 * 
 *  a PHP micro framework.
 * 
 *  For more informations: {@link https://github.com/sofadesign/limonade}
 *  
 *  @author Fabrice Luraine
 *  @copyright Copyright (c) 2009 Fabrice Luraine
 *  @license http://opensource.org/licenses/mit-license.php The MIT License
 *  @package limonade
 */

#   -----------------------------------------------------------------------    #
#    Copyright (c) 2009 Fabrice Luraine                                        #
#                                                                              #
#    Permission is hereby granted, free of charge, to any person               #
#    obtaining a copy of this software and associated documentation            #
#    files (the "Software"), to deal in the Software without                   #
#    restriction, including without limitation the rights to use,              #
#    copy, modify, merge, publish, distribute, sublicense, and/or sell         #
#    copies of the Software, and to permit persons to whom the                 #
#    Software is furnished to do so, subject to the following                  #
#    conditions:                                                               #
#                                                                              #
#    The above copyright notice and this permission notice shall be            #
#    included in all copies or substantial portions of the Software.           #
#                                                                              #
#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,           #
#    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES           #
#    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                  #
#    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT               #
#    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,              #
#    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING              #
#    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR             #
#    OTHER DEALINGS IN THE SOFTWARE.                                           #
# ============================================================================ # 








# ============================================================================ #
#    0. PREPARE                                                                #
# ============================================================================ #

## CONSTANTS __________________________________________________________________
/**
 * Limonade version
 */
define('LIMONADE',              '0.5.0');
define('LIM_NAME',              'Un grand cru qui sait se faire attendre');
define('LIM_START_MICROTIME',   (float)substr(microtime(), 0, 10));
define('LIM_SESSION_NAME',      'LIMONADE'.str_replace('.','x',LIMONADE));
define('LIM_SESSION_FLASH_KEY', '_lim_flash_messages');
if(function_exists('memory_get_usage'))
	define('LIM_START_MEMORY',      memory_get_usage());
define('E_LIM_HTTP',            32768);
define('E_LIM_PHP',             65536);
define('E_LIM_DEPRECATED',      35000);
define('NOT_FOUND',             404);
define('SERVER_ERROR',          500);
define('ENV_PRODUCTION',        10);
define('ENV_DEVELOPMENT',       100);
define('X-SENDFILE',            10);
define('X-LIGHTTPD-SEND-FILE',  20);

# for PHP 5.3.0 <
if(!defined('E_DEPRECATED'))         define('E_DEPRECATED',        8192);
if(!defined('E_USER_DEPRECATED'))    define('E_USER_DEPRECATED',   16384);
# for PHP 5.2.0 <
if (!defined('E_RECOVERABLE_ERROR')) define('E_RECOVERABLE_ERROR', 4096);


## SETTING BASIC SECURITY _____________________________________________________

# A. Unsets all global variables set from a superglobal array

/**
 * @access private
 * @return void
 */
function unregister_globals()
{
  $args = func_get_args();
  foreach($args as $k => $v)
    if(array_key_exists($k, $GLOBALS)) unset($GLOBALS[$k]);
}

if(ini_get('register_globals'))
{
  unregister_globals( '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER', 
                      '_ENV', '_FILES');
  ini_set('register_globals', 0);
}

# B. removing magic quotes

/**
 * @access private
 * @param string $array 
 * @return array
 */
function remove_magic_quotes($array)
{
  foreach ($array as $k => $v)
    $array[$k] = is_array($v) ? remove_magic_quotes($v) : stripslashes($v);
  return $array;
}

if (get_magic_quotes_gpc())
{
  $_GET    = remove_magic_quotes($_GET);
  $_POST   = remove_magic_quotes($_POST);
  $_COOKIE = remove_magic_quotes($_COOKIE);
  ini_set('magic_quotes_gpc', 0);
}

if(function_exists('set_magic_quotes_runtime') && get_magic_quotes_runtime()) set_magic_quotes_runtime(false);

# C. Disable error display
#    by default, no error reporting; it will be switched on later in run().
#    ini_set('display_errors', 1); must be called explicitly in app file
#    if you want to show errors before running app
ini_set('display_errors', 0);

## SETTING INTERNAL ROUTES _____________________________________________________

dispatch(array("/_lim_css/*.css", array('_lim_css_filename')), 'render_limonade_css');
  /**
   * Internal controller that responds to route /_lim_css/*.css
   *
   * @access private
   * @return string
   */
  function render_limonade_css()
  {
    option('views_dir', file_path(option('limonade_public_dir'), 'css'));
    $fpath = file_path(params('_lim_css_filename').".css");
    return css($fpath, null); // with no layout
  }

dispatch(array("/_lim_public/**", array('_lim_public_file')), 'render_limonade_file');
  /**
   * Internal controller that responds to route /_lim_public/**
   *
   * @access private
   * @return void
   */
  function render_limonade_file()
  {
    $fpath = file_path(option('limonade_public_dir'), params('_lim_public_file'));
    return render_file($fpath, true);
  }




                                     # # #




# ============================================================================ #
#    1. BASE                                                                   #
# ============================================================================ #
 
## ABSTRACTS ___________________________________________________________________

# Abstract methods that might be redefined by user:
#
# - function configure(){}
# - function initialize(){}
# - function autoload_controller($callback){}
# - function before($route){}
# - function after($output, $route){}
# - function not_found($errno, $errstr, $errfile=null, $errline=null){}
# - function server_error($errno, $errstr, $errfile=null, $errline=null){}
# - function route_missing($request_method, $request_uri){}
# - function before_exit(){}
# - function before_render($content_or_func, $layout, $locals, $view_path){}
# - function autorender($route){}
# - function before_sending_header($header){}
#
# See abstract.php for more details.


## MAIN PUBLIC FUNCTIONS _______________________________________________________

/**
 * Set and returns options values
 * 
 * If multiple values are provided, set $name option with an array of those values.
 * If there is only one value, set $name option with the provided $values
 *
 * @param string $name 
 * @param mixed  $values,... 
 * @return mixed option value for $name if $name argument is provided, else return all options
 */
function option($name = null, $values = null)
{
  static $options = array();
  $args = func_get_args();
  $name = array_shift($args);
  if(is_null($name)) return $options;
  if(!empty($args))
  {
    $options[$name] = count($args) > 1 ? $args : $args[0];
  }
  if(array_key_exists($name, $options)) return $options[$name];
  return;
}

/**
 * Set and returns params
 * 
 * Depending on provided arguments:
 * 
 *  * Reset params if first argument is null
 * 
 *  * If first argument is an array, merge it with current params
 * 
 *  * If there is a second argument $value, set param $name (first argument) with $value
 * <code>
 *  params('name', 'Doe') // set 'name' => 'Doe'
 * </code>
 *  * If there is more than 2 arguments, set param $name (first argument) value with
 *    an array of next arguments
 * <code>
 *  params('months', 'jan', 'feb', 'mar') // set 'month' => array('months', 'jan', 'feb', 'mar')
 * </code>
 * 
 * @param mixed $name_or_array_or_null could be null || array of params || name of a param (optional)
 * @param mixed $value,... for the $name param (optional)
 * @return mixed all params, or one if a first argument $name is provided
 */
function params($name_or_array_or_null = null, $value = null)
{
  static $params = array();
  $args = func_get_args();

  if(func_num_args() > 0)
  {
    $name = array_shift($args);
    if(is_null($name))
    {
      # Reset params
      $params = array();
      return $params;
    }
    if(is_array($name))
    {
      $params = array_merge($params, $name);
      return $params;
    }
    $nargs = count($args);
    if($nargs > 0)
    {
      $value = $nargs > 1 ? $args : $args[0];
      $params[$name] = $value;
    }
    return array_key_exists($name,$params) ? $params[$name] : null;
  }

  return $params;
}

/**
 * Set and returns template variables
 * 
 * If multiple values are provided, set $name variable with an array of those values.
 * If there is only one value, set $name variable with the provided $values
 *
 * @param string $name 
 * @param mixed  $values,... 
 * @return mixed variable value for $name if $name argument is provided, else return all variables
 */
function set($name = null, $values = null)
{
  static $vars = array();
  $args = func_get_args();
  $name = array_shift($args);
  if(is_null($name)) return $vars;
  if(!empty($args))
  {
    $vars[$name] = count($args) > 1 ? $args : $args[0];
  }
  if(array_key_exists($name, $vars)) return $vars[$name];
  return $vars;
}

/**
 * Sets a template variable with a value or a default value if value is empty
 *
 * @param string $name 
 * @param string $value 
 * @param string $default 
 * @return mixed setted value
 */
function set_or_default($name, $value, $default)
{
  return set($name, value_or_default($value, $default));
}

/**
 * Running application
 *
 * @param string $env 
 * @return void
 */
function run($env = null)
{
  if(is_null($env)) $env = env();
   
  # 0. Set default configuration
  $root_dir  = dirname(app_file());
  $lim_dir   = dirname(__FILE__);
  $base_path = dirname(file_path($env['SERVER']['SCRIPT_NAME']));
  $base_file = basename($env['SERVER']['SCRIPT_NAME']);
  $base_uri  = file_path($base_path, (($base_file == 'index.php') ? '?' : $base_file.'?'));
  
  option('root_dir',           $root_dir);
  option('limonade_dir',       file_path($lim_dir));
  option('limonade_views_dir', file_path($lim_dir, 'limonade', 'views'));
  option('limonade_public_dir',file_path($lim_dir, 'limonade', 'public'));
  option('public_dir',         file_path($root_dir, 'public'));
  option('views_dir',          file_path($root_dir, 'views'));
  option('controllers_dir',    file_path($root_dir, 'controllers'));
  option('lib_dir',            file_path($root_dir, 'lib'));
  option('error_views_dir',    option('limonade_views_dir'));
  option('base_path',          $base_path);
  option('base_uri',           $base_uri); // set it manually if you use url_rewriting
  option('env',                ENV_PRODUCTION);
  option('debug',              true);
  option('session',            LIM_SESSION_NAME); // true, false or the name of your session
  option('encoding',           'utf-8');
  option('signature',          LIM_NAME); // X-Limonade header value or false to hide it
  option('gzip',               false);
  option('x-sendfile',         0); // 0: disabled, 
                                   // X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
                                   // X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5


  # 1. Set handlers
  # 1.1 Set error handling
  ini_set('display_errors', 1);
  set_error_handler('error_handler_dispatcher', E_ALL ^ E_NOTICE);
  
  # 1.2 Register shutdown function
  register_shutdown_function('stop_and_exit');

  # 2. Set user configuration
  call_if_exists('configure');
  
  # 2.1 Set gzip compression if defined
  if(is_bool(option('gzip')) && option('gzip'))
  {
    ini_set('zlib.output_compression', '1');
  }
  
  # 2.2 Set X-Limonade header
  if($signature = option('signature')) send_header("X-Limonade: $signature");

  # 3. Loading libs
  require_once_dir(option('lib_dir'));
  fallbacks_for_not_implemented_functions();

  # 4. Starting session
  if(!defined('SID') && option('session'))
  {
    if(!is_bool(option('session'))) session_name(option('session'));
    if(!session_start()) trigger_error("An error occured while trying to start the session", E_USER_WARNING);
  }

  # 5. Set some default methods if needed
  if(!function_exists('after'))
  {
    function after($output)
    {
      return $output;
    }
  }
  if(!function_exists('route_missing'))
  {
    function route_missing($request_method, $request_uri)
    {
      halt(NOT_FOUND, "($request_method) $request_uri");
    }
  }

  call_if_exists('initialize');

  # 6. Check request
  if($rm = request_method($env))
  {
    if(request_is_head($env)) ob_start(); // then no output

    if(!request_method_is_allowed($rm))
      halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");

    # 6.1 Check matching route
    if($route = route_find($rm, request_uri($env)))
    {
      params($route['params']);

      # 6.2 Load controllers dir
      if(!function_exists('autoload_controller'))
      {
        function autoload_controller($callback)
        {
          require_once_dir(option('controllers_dir'));
        }
      }
      autoload_controller($route['callback']);

      if(is_callable($route['callback']))
      {
        # 6.3 Call before function
        call_if_exists('before', $route);

        # 6.4 Call matching controller function and output result
        $output = call_user_func_array($route['callback'], array_values($route['params']));
        if(is_null($output)) $output = call_if_exists('autorender', $route);
        echo after(error_notices_render() . $output, $route);
      }
      else halt(SERVER_ERROR, "Routing error: undefined function '{$route['callback']}'", $route);      
    }
    else route_missing($rm, request_uri($env));

  }
  else halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
}

/**
 * Stop and exit limonade application
 *
 * @access private 
 * @param boolean exit or not
 * @return void
 */
function stop_and_exit($exit = true)
{
  call_if_exists('before_exit', $exit);
  $headers = headers_list();
  if(request_is_head())
  { 
     ob_end_clean();
  } else {
    $flash_sweep = true;
    foreach($headers as $header)
    {
      // If a Content-Type header exists, flash_sweep only if is text/html
      // Else if there's no Content-Type header, flash_sweep by default
      if(stripos($header, 'Content-Type:') === 0)
      {
        $flash_sweep = stripos($header, 'Content-Type: text/html') === 0;
        break;
      }
    }
    if($flash_sweep) flash_sweep();
  }
  if(defined('SID')) session_write_close();
  if($exit) exit;
}

/**
 * Returns limonade environment variables:
 *
 * 'SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE', 
 * 'GET', 'POST', 'PUT', 'DELETE'
 * 
 * If a null argument is passed, reset and rebuild environment
 *
 * @param null @reset reset and rebuild environment
 * @return array
 */
function env($reset = null)
{
  static $env = array();
  if(func_num_args() > 0)
  {
    $args = func_get_args();
    if(is_null($args[0])) $env = array();
  }

  if(empty($env))
  {
    if(empty($GLOBALS['_SERVER']))
    {
      // Fixing empty $GLOBALS['_SERVER'] bug 
      // http://sofadesign.lighthouseapp.com/projects/29612-limonade/tickets/29-env-is-empty
      $GLOBALS['_SERVER']  =& $_SERVER;
      $GLOBALS['_FILES']   =& $_FILES;
      $GLOBALS['_REQUEST'] =& $_REQUEST;
      $GLOBALS['_SESSION'] =& $_SESSION;
      $GLOBALS['_ENV']     =& $_ENV;
      $GLOBALS['_COOKIE']  =& $_COOKIE;
    }

    $glo_names = array('SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE');

    $vars = array_merge($glo_names, request_methods());
    foreach($vars as $var)
    {
      $varname = "_$var";
      if(!array_key_exists($varname, $GLOBALS)) $GLOBALS[$varname] = array();
      $env[$var] =& $GLOBALS[$varname];
    }

    $method = request_method($env);
    if($method == 'PUT' || $method == 'DELETE')
    {
      $varname = "_$method";
      if(array_key_exists('_method', $_POST) && $_POST['_method'] == $method)
      {
        foreach($_POST as $k => $v)
        {
          if($k == "_method") continue;
          $GLOBALS[$varname][$k] = $v;
        }
      }
      else
      {
        parse_str(file_get_contents('php://input'), $GLOBALS[$varname]);
      }
    }
  }
  return $env;
}

/**
 * Returns application root file path
 *
 * @return string
 */
function app_file()
{
  static $file;
  if(empty($file))
  {
    $debug_backtrace = debug_backtrace();
    $stacktrace = array_pop($debug_backtrace);
    $file = $stacktrace['file'];
  }
  return file_path($file);
}




                                     # # #




# ============================================================================ #
#    2. ERROR                                                                  #
# ============================================================================ #
 
/**
 * Associate a function with error code(s) and return all associations
 *
 * @param string $errno 
 * @param string $function 
 * @return array
 */
function error($errno = null, $function = null)
{
  static $errors = array();
  if(func_num_args() > 0)
  {
    $errors[] = array('errno'=>$errno, 'function'=> $function);
  }
  return $errors;
}

/**
 * Raise an error, passing a given error number and an optional message,
 * then exit.
 * Error number should be a HTTP status code or a php user error (E_USER...)
 * $errno and $msg arguments can be passsed in any order
 * If no arguments are passed, default $errno is SERVER_ERROR (500)
 *
 * @param int,string $errno Error number or message string
 * @param string,string $msg Message string or error number
 * @param mixed $debug_args extra data provided for debugging
 * @return void
 */
function halt($errno = SERVER_ERROR, $msg = '', $debug_args = null)
{
  $args = func_get_args();
  $error = array_shift($args);

  # switch $errno and $msg args
  # TODO cleanup / refactoring
  if(is_string($errno))
  {
   $msg = $errno;
   $oldmsg = array_shift($args);
   $errno = empty($oldmsg) ? SERVER_ERROR : $oldmsg;
  }
  else if(!empty($args)) $msg = array_shift($args);

  if(empty($msg) && $errno == NOT_FOUND) $msg = request_uri();
  if(empty($msg)) $msg = "";
  if(!empty($args)) $debug_args = $args;
  set('_lim_err_debug_args', $debug_args);

  error_handler_dispatcher($errno, $msg, null, null);

}

/**
 * Internal error handler dispatcher
 * Find and call matching error handler and exit
 * If no match found, call default error handler
 *
 * @access private
 * @param int $errno 
 * @param string $errstr 
 * @param string $errfile 
 * @param string $errline 
 * @return void
 */
function error_handler_dispatcher($errno, $errstr, $errfile, $errline)
{
  $back_trace = debug_backtrace();
  while($trace = array_shift($back_trace))
  {
    if($trace['function'] == 'halt')
    {
      $errfile = $trace['file'];
      $errline = $trace['line'];
      break;
    }
  }  

  # Notices and warning won't halt execution
  if(error_wont_halt_app($errno))
  {
    error_notice($errno, $errstr, $errfile, $errline);
  	return;
  }
  else
  {
    # Other errors will stop application
    static $handlers = array();
    if(empty($handlers))
    {
      error(E_LIM_PHP, 'error_default_handler');
      $handlers = error();
    }
    
    $is_http_err = http_response_status_is_valid($errno);
    while($handler = array_shift($handlers))
    {
      $e = is_array($handler['errno']) ? $handler['errno'] : array($handler['errno']);
      while($ee = array_shift($e))
      {
        if($ee == $errno || $ee == E_LIM_PHP || ($ee == E_LIM_HTTP && $is_http_err))
        {
          echo call_if_exists($handler['function'], $errno, $errstr, $errfile, $errline);
          exit;
        }
      }
    }
  }
}


/**
 * Default error handler
 *
 * @param string $errno 
 * @param string $errstr 
 * @param string $errfile 
 * @param string $errline 
 * @return string error output
 */
function error_default_handler($errno, $errstr, $errfile, $errline)
{
  $is_http_err = http_response_status_is_valid($errno);
  $http_error_code = $is_http_err ? $errno : SERVER_ERROR;

  status($http_error_code);

  return $http_error_code == NOT_FOUND ?
            error_not_found_output($errno, $errstr, $errfile, $errline) :
            error_server_error_output($errno, $errstr, $errfile, $errline);                    
}

/**
 * Returns not found error output
 *
 * @access private
 * @param string $msg 
 * @return string
 */
function error_not_found_output($errno, $errstr, $errfile, $errline)
{
  if(!function_exists('not_found'))
  {
    /**
     * Default not found error output
     *
     * @param string $errno 
     * @param string $errstr 
     * @param string $errfile 
     * @param string $errline 
     * @return string
     */
    function not_found($errno, $errstr, $errfile=null, $errline=null)
    {
      option('views_dir', option('error_views_dir'));
      $msg = h(rawurldecode($errstr));
      return html("<h1>Page not found:</h1><p><code>{$msg}</code></p>", error_layout());
    }
  }
  return not_found($errno, $errstr, $errfile, $errline);
}

/**
 * Returns server error output
 *
 * @access private
 * @param int $errno 
 * @param string $errstr 
 * @param string $errfile 
 * @param string $errline 
 * @return string
 */
function error_server_error_output($errno, $errstr, $errfile, $errline)
{
  if(!function_exists('server_error'))
  {
    /**
     * Default server error output
     *
     * @param string $errno 
     * @param string $errstr 
     * @param string $errfile 
     * @param string $errline 
     * @return string
     */
    function server_error($errno, $errstr, $errfile=null, $errline=null)
    {
      $is_http_error = http_response_status_is_valid($errno);
      $args = compact('errno', 'errstr', 'errfile', 'errline', 'is_http_error');
      option('views_dir', option('limonade_views_dir'));
      $html = render('error.html.php', null, $args);	
      option('views_dir', option('error_views_dir'));
      return html($html, error_layout(), $args);
    }
  }
  return server_error($errno, $errstr, $errfile, $errline);
}

/**
 * Set and returns error output layout
 *
 * @param string $layout 
 * @return string
 */
function error_layout($layout = false)
{
  static $o_layout = 'default_layout.php';
  if($layout !== false)
  {
    option('error_views_dir', option('views_dir'));
    $o_layout = $layout;
  }
  return $o_layout;
}


/**
 * Set a notice if arguments are provided
 * Returns all stored notices.
 * If $errno argument is null, reset the notices array
 *
 * @access private
 * @param string, null $str 
 * @return array
 */
function error_notice($errno = false, $errstr = null, $errfile = null, $errline = null)
{
  static $notices = array();
  if($errno) $notices[] = compact('errno', 'errstr', 'errfile', 'errline');
  else if(is_null($errno)) $notices = array();
  return $notices;
}

/**
 * Returns notices output rendering and reset notices
 *
 * @return string
 */
function error_notices_render()
{
  if(option('debug') && option('env') > ENV_PRODUCTION)
  {
    $notices = error_notice();
    error_notice(null); // reset notices
    $c_view_dir = option('views_dir'); // keep for restore after render
    option('views_dir', option('limonade_views_dir'));
    $o = render('_notices.html.php', null, array('notices' => $notices));
    option('views_dir', $c_view_dir); // restore current views dir

    return $o;
  }
}

/**
 * Checks if an error is will halt application execution. 
 * Notices and warnings will not.
 *
 * @access private
 * @param string $num error code number
 * @return boolean
 */
function error_wont_halt_app($num)
{
  return $num == E_NOTICE ||
         $num == E_WARNING ||
         $num == E_CORE_WARNING ||
         $num == E_COMPILE_WARNING ||
         $num == E_USER_WARNING ||
         $num == E_USER_NOTICE ||
         $num == E_DEPRECATED ||
         $num == E_USER_DEPRECATED ||
         $num == E_LIM_DEPRECATED;
}



/**
 * return error code name for a given code num, or return all errors names
 *
 * @param string $num 
 * @return mixed
 */
function error_type($num = null)
{
  $types = array (
              E_ERROR              => 'ERROR',
              E_WARNING            => 'WARNING',
              E_PARSE              => 'PARSING ERROR',
              E_NOTICE             => 'NOTICE',
              E_CORE_ERROR         => 'CORE ERROR',
              E_CORE_WARNING       => 'CORE WARNING',
              E_COMPILE_ERROR      => 'COMPILE ERROR',
              E_COMPILE_WARNING    => 'COMPILE WARNING',
              E_USER_ERROR         => 'USER ERROR',
              E_USER_WARNING       => 'USER WARNING',
              E_USER_NOTICE        => 'USER NOTICE',
              E_STRICT             => 'STRICT NOTICE',
              E_RECOVERABLE_ERROR  => 'RECOVERABLE ERROR',
              E_DEPRECATED         => 'DEPRECATED WARNING',
              E_USER_DEPRECATED    => 'USER DEPRECATED WARNING',
              E_LIM_DEPRECATED     => 'LIMONADE DEPRECATED WARNING'
              );
  return is_null($num) ? $types : $types[$num];
}

/**
 * Returns http response status for a given error number
 *
 * @param string $errno 
 * @return int
 */
function error_http_status($errno)
{
  $code = http_response_status_is_valid($errno) ? $errno : SERVER_ERROR;
  return http_response_status($code);
}

                                     # # #




# ============================================================================ #
#    3. REQUEST                                                                #
# ============================================================================ #
 
/**
 * Returns current request method for a given environment or current one
 *
 * @param string $env 
 * @return string
 */
function request_method($env = null)
{
  if(is_null($env)) $env = env();
  $m = array_key_exists('REQUEST_METHOD', $env['SERVER']) ? $env['SERVER']['REQUEST_METHOD'] : null;
  if($m == "POST" && array_key_exists('_method', $env['POST'])) 
    $m = strtoupper($env['POST']['_method']);
  if(!in_array(strtoupper($m), request_methods()))
  {
    trigger_error("'$m' request method is unknown or unavailable.", E_USER_WARNING);
    $m = false;
  }
  return $m;
}

/**
 * Checks if a request method or current one is allowed
 *
 * @param string $m 
 * @return bool
 */
function request_method_is_allowed($m = null)
{
  if(is_null($m)) $m = request_method();
  return in_array(strtoupper($m), request_methods());
}

/**
 * Checks if request method is GET
 *
 * @param string $env 
 * @return bool
 */
function request_is_get($env = null)
{
  return request_method($env) == "GET";
}

/**
 * Checks if request method is POST
 *
 * @param string $env 
 * @return bool
 */
function request_is_post($env = null)
{
  return request_method($env) == "POST";
}

/**
 * Checks if request method is PUT
 *
 * @param string $env 
 * @return bool
 */
function request_is_put($env = null)
{
  return request_method($env) == "PUT";
}

/**
 * Checks if request method is DELETE
 *
 * @param string $env 
 * @return bool
 */
function request_is_delete($env = null)
{
  return request_method($env) == "DELETE";
}

/**
 * Checks if request method is HEAD
 *
 * @param string $env 
 * @return bool
 */
function request_is_head($env = null)
{
  return request_method($env) == "HEAD";
}

/**
 * Returns allowed request methods
 *
 * @return array
 */
function request_methods()
{
  return array("GET","POST","PUT","DELETE", "HEAD");
}

/**
 * Returns current request uri (the path that will be compared with routes)
 * 
 * (Inspired from codeigniter URI::_fetch_uri_string method)
 *
 * @return string
 */
function request_uri($env = null)
{
  static $uri = null;
  if(is_null($env))
  {
    if(!is_null($uri)) return $uri;
    $env = env();
  }

  if(array_key_exists('uri', $env['GET']))
  {
    $uri = $env['GET']['uri'];
  }
  else if(array_key_exists('u', $env['GET']))
  {
    $uri = $env['GET']['u'];
  }
  // bug: dot are converted to _... so we can't use it...
  // else if (count($env['GET']) == 1 && trim(key($env['GET']), '/') != '')
  // {
  //  $uri = key($env['GET']);
  // }
  else
  {
    $app_file = app_file();
    $path_info = isset($env['SERVER']['PATH_INFO']) ? $env['SERVER']['PATH_INFO'] : @getenv('PATH_INFO');
    $query_string =  isset($env['SERVER']['QUERY_STRING']) ? $env['SERVER']['QUERY_STRING'] : @getenv('QUERY_STRING');

    // Is there a PATH_INFO variable?
    // Note: some servers seem to have trouble with getenv() so we'll test it two ways
    if (trim($path_info, '/') != '' && $path_info != "/".$app_file)
    {
      if(strpos($path_info, '&') !== 0)
      {
        # exclude GET params
        $params = explode('&', $path_info);
        $path_info = array_shift($params);
        # populate $_GET
        foreach($params as $param)
        {
          if(strpos($param, '=') > 0)
          {
            list($k, $v) = explode('=', $param);
            $env['GET'][$k] = $v;
          }
        }
      }
      $uri = $path_info;
    }
    // No PATH_INFO?... What about QUERY_STRING?
    elseif (trim($query_string, '/') != '' && $query_string[0] == '/')
    {
      $uri = $query_string;
      $get = $env['GET'];
      if(count($get) > 0)
      {
        # exclude GET params
        $keys  = array_keys($get);
        $first = array_shift($keys);
        if(strpos($query_string, $first) === 0) $uri = $first;
      }
    }
    elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
    {
      $request_uri = rtrim($env['SERVER']['REQUEST_URI'], '?/').'/';
      $base_path = $env['SERVER']['SCRIPT_NAME'];

      if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
      $uri = str_replace($base_path, '', $request_uri);
      if(option('base_uri') && strpos($uri, option('base_uri')) === 0) {
       $uri = substr($uri, strlen(option('base_uri')));
      }
      if(strpos($uri, '?') !== false) {
      	$uri = substr($uri, 0, strpos($uri, '?')) . '/';
      }
    }
    elseif($env['SERVER']['argc'] > 1 && trim($env['SERVER']['argv'][1], '/') != '')
    {
      $uri = $env['SERVER']['argv'][1];
    }
  }

  $uri = rtrim($uri, "/"); # removes ending /
  if(empty($uri))
  {
    $uri = '/';
  }
  else if($uri[0] != '/')
  {
    $uri = '/' . $uri; # add a leading slash
  }
  return rawurldecode($uri);
}




                                     # # #




# ============================================================================ #
#    4. ROUTER                                                                 #
# ============================================================================ #
 
/**
 * An alias of {@link dispatch_get()}
 *
 * @return void
 */
function dispatch($path_or_array, $callback, $options = array())
{
  dispatch_get($path_or_array, $callback, $options);
}

/**
 * Add a GET route. Also automatically defines a HEAD route.
 *
 * @param string $path_or_array 
 * @param string $callback
 * @param array $options (optional). See {@link route()} for available options.
 * @return void
 */
function dispatch_get($path_or_array, $callback, $options = array())
{
  route("GET", $path_or_array, $callback, $options);
  route("HEAD", $path_or_array, $callback, $options);
}

/**
 * Add a POST route
 *
 * @param string $path_or_array 
 * @param string $callback
 * @param array $options (optional). See {@link route()} for available options.
 * @return void
 */
function dispatch_post($path_or_array, $callback, $options = array())
{
  route("POST", $path_or_array, $callback, $options);
}

/**
 * Add a PUT route
 *
 * @param string $path_or_array 
 * @param string $callback
 * @param array $options (optional). See {@link route()} for available options.
 * @return void
 */
function dispatch_put($path_or_array, $callback, $options = array())
{
  route("PUT", $path_or_array, $callback, $options);
}

/**
 * Add a DELETE route
 *
 * @param string $path_or_array 
 * @param string $callback
 * @param array $options (optional). See {@link route()} for available options.
 * @return void
 */
function dispatch_delete($path_or_array, $callback, $options = array())
{
  route("DELETE", $path_or_array, $callback, $options);
}


/**
 * Add route if required params are provided.
 * Delete all routes if null is passed as a unique argument
 * Return all routes
 * 
 * @see route_build()
 * @access private
 * @param string $method 
 * @param string|array $path_or_array 
 * @param callback $func
 * @param array $options (optional). Available options: 
 *   - 'params' key with an array of parameters: for parametrized routes.
 *     those parameters will be merged with routes parameters.
 * @return array
 */
function route()
{
  static $routes = array();
  $nargs = func_num_args();
  if( $nargs > 0)
  {
    $args = func_get_args();
    if($nargs === 1 && is_null($args[0])) $routes = array();
    else if($nargs < 3) trigger_error("Missing arguments for route()", E_USER_ERROR);
    else
    {
      $method        = $args[0];
      $path_or_array = $args[1];
      $func          = $args[2];
      $options       = $nargs > 3 ? $args[3] : array();

      $routes[] = route_build($method, $path_or_array, $func, $options);
    }
  }
  return $routes;
}

/**
 * An alias of route(null): reset all routes
 * 
 * @access private
 * @return void
 */
function route_reset()
{
  route(null);
}

/**
 * Build a route and return it
 *
 * @access private
 * @param string $method allowed http method (one of those returned by {@link request_methods()})
 * @param string|array $path_or_array 
 * @param callback $callback callback called when route is found. It can be
 *   a function, an object method, a static method or a closure.
 *   See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
 *   to learn more about callbacks.
 * @param array $options (optional). Available options: 
 *   - 'params' key with an array of parameters: for parametrized routes.
 *     those parameters will be merged with routes parameters.
 * @return array array with keys "method", "pattern", "names", "callback", "options"
 */
function route_build($method, $path_or_array, $callback, $options = array())
{
  $method = strtoupper($method);
  if(!in_array($method, request_methods())) 
    trigger_error("'$method' request method is unkown or unavailable.", E_USER_WARNING);

  if(is_array($path_or_array))
  {
    $path  = array_shift($path_or_array);
    $names = $path_or_array[0];
  }
  else
  {
    $path  = $path_or_array;
    $names = array();
  }

  $single_asterisk_subpattern   = "(?:/([^\/]*))?";
  $double_asterisk_subpattern   = "(?:/(.*))?";
  $optionnal_slash_subpattern   = "(?:/*?)";
  $no_slash_asterisk_subpattern = "(?:([^\/]*))?";

  if($path[0] == "^")
  {
    if($path{strlen($path) - 1} != "$") $path .= "$";
     $pattern = "#".$path."#i";
  }
  else if(empty($path) || $path == "/")
  {
    $pattern = "#^".$optionnal_slash_subpattern."$#";
  }
  else
  {
    $parsed = array();
    $elts = explode('/', $path);

    $parameters_count = 0;

    foreach($elts as $elt)
    {
      if(empty($elt)) continue;

      $name = null; 

      # extracting double asterisk **
      if($elt == "**"):
        $parsed[] = $double_asterisk_subpattern;
        $name = $parameters_count;

      # extracting single asterisk *
      elseif($elt == "*"):
        $parsed[] = $single_asterisk_subpattern;
        $name = $parameters_count;

      # extracting named parameters :my_param 
      elseif($elt[0] == ":"):
        if(preg_match('/^:([^\:]+)$/', $elt, $matches))
        {
          $parsed[] = $single_asterisk_subpattern;
          $name = $matches[1];
        };

      elseif(strpos($elt, '*') !== false):
        $sub_elts = explode('*', $elt);
        $parsed_sub = array();
        foreach($sub_elts as $sub_elt)
        {
          $parsed_sub[] = preg_quote($sub_elt, "#");
          $name = $parameters_count;
        }
        // 
        $parsed[] = "/".implode($no_slash_asterisk_subpattern, $parsed_sub);

      else:
        $parsed[] = "/".preg_quote($elt, "#");

      endif;

      /* set parameters names */ 
      if(is_null($name)) continue;
      if(!array_key_exists($parameters_count, $names) || is_null($names[$parameters_count]))
        $names[$parameters_count] = $name;
      $parameters_count++;
    }

    $pattern = "#^".implode('', $parsed).$optionnal_slash_subpattern."?$#i";
  }

  return array( "method"       => $method,
                "pattern"      => $pattern,
                "names"        => $names,
                "callback"     => $callback,
                "options"      => $options  );
}

/**
 * Find a route and returns it.
 * Parameters values extracted from the path are added and merged 
 * with the default 'params' option of the route
 * If not found, returns false.
 * Routes are checked from first added to last added.
 *
 * @access private
 * @param string $method 
 * @param string $path
 * @return array,false route array has same keys as route returned by 
 *  {@link route_build()} ("method", "pattern", "names", "callback", "options")
 *  + the processed "params" key
 */
function route_find($method, $path)
{
  $routes = route();
  $method = strtoupper($method);
  foreach($routes as $route)
  {
    if($method == $route["method"] && preg_match($route["pattern"], $path, $matches))
    {
      $options = $route["options"];
      $params = array_key_exists('params', $options) ? $options["params"] : array();
      if(count($matches) > 1)
      {
        array_shift($matches);
        $n_matches = count($matches);
        $names     = array_values($route["names"]);
        $n_names   = count($names);
        if( $n_matches < $n_names )
        {
          $a = array_fill(0, $n_names - $n_matches, null);
          $matches = array_merge($matches, $a);
        }
        else if( $n_matches > $n_names )
        {
          $names = range($n_names, $n_matches - 1);
        }
        $arr_comb = array_combine($names, $matches);
        $params = array_replace($params, $arr_comb);
      }
      $route["params"] = $params;
      return $route;
    }
  }
  return false;
}





# ============================================================================ #
#    5. OUTPUT AND RENDERING                                                   #
# ============================================================================ #

/**
 * Returns a string to output
 * 
 * It might use a template file, a function, or a formatted string (like {@link sprintf()}).
 * It could be embraced by a layout or not.
 * Local vars can be passed in addition to variables made available with the {@link set()}
 * function.
 *
 * @param string $content_or_func 
 * @param string $layout 
 * @param string $locals 
 * @return string
 */
function render($content_or_func, $layout = '', $locals = array())
{
  $args = func_get_args();
  $content_or_func = array_shift($args);
  $layout = count($args) > 0 ? array_shift($args) : layout();
  $view_path = file_path(option('views_dir'),$content_or_func);
  
  if(function_exists('before_render'))
    list($content_or_func, $layout, $locals, $view_path) = before_render($content_or_func, $layout, $locals, $view_path);    
  
  $vars = array_merge(set(), $locals);

  $flash = flash_now();
  if(array_key_exists('flash', $vars)) trigger_error('A $flash variable is already passed to view. Flash messages will only be accessible through flash_now()', E_USER_NOTICE);  
  else if(!empty($flash)) $vars['flash'] = $flash;

  $infinite_loop = false;

  # Avoid infinite loop: this function is in the backtrace ?
  if(function_exists($content_or_func))
  {
    $back_trace = debug_backtrace();
    while($trace = array_shift($back_trace))
    {
      if($trace['function'] == strtolower($content_or_func))
      {
        $infinite_loop = true;
        break;
      }
    }
  }

  if(function_exists($content_or_func) && !$infinite_loop)
  {
    ob_start();
    call_user_func($content_or_func, $vars);
    $content = ob_get_clean();
  }
  elseif(file_exists($view_path))
  {
    ob_start();
    extract($vars);
    include $view_path;
    $content = ob_get_clean();
  }
  else
  {
    if(substr_count($content_or_func, '%') !== count($vars)) $content = $content_or_func;
    else $content = vsprintf($content_or_func, $vars);
  }

  if(empty($layout)) return $content;

  return render($layout, null, array('content' => $content));
}

/**
 * Returns a string to output
 * 
 * Shortcut to render with no layout.
 *
 * @param string $content_or_func 
 * @param string $locals 
 * @return string
 */
function partial($content_or_func, $locals = array())
{
  return render($content_or_func, null, $locals);
}

/**
 * Returns html output with proper http headers
 *
 * @param string $content_or_func 
 * @param string $layout 
 * @param string $locals 
 * @return string
 */ 
function html($content_or_func, $layout = '', $locals = array())
{
  send_header('Content-Type: text/html; charset='.strtolower(option('encoding')));
  $args = func_get_args();
  return call_user_func_array('render', $args);
}

/**
 * Set and return current layout
 *
 * @param string $function_or_file 
 * @return string
 */
function layout($function_or_file = null)
{
  static $layout = null;
  if(func_num_args() > 0) $layout = $function_or_file;
  return $layout;
}

/**
 * Returns xml output with proper http headers
 *
 * @param string $content_or_func 
 * @param string $layout 
 * @param string $locals 
 * @return string
 */
function xml($data)
{
  send_header('Content-Type: text/xml; charset='.strtolower(option('encoding')));
  $args = func_get_args();
  return call_user_func_array('render', $args);
}

/**
 * Returns css output with proper http headers
 *
 * @param string $content_or_func 
 * @param string $layout 
 * @param string $locals 
 * @return string
 */
function css($content_or_func, $layout = '', $locals = array())
{
  send_header('Content-Type: text/css; charset='.strtolower(option('encoding')));
  $args = func_get_args();
  return call_user_func_array('render', $args);
}

/**
 * Returns javacript output with proper http headers
 *
 * @param string $content_or_func 
 * @param string $layout 
 * @param string $locals 
 * @return string
 */
function js($content_or_func, $layout = '', $locals = array())
{
  send_header('Content-Type: application/javascript; charset='.strtolower(option('encoding')));
  $args = func_get_args();
  return call_user_func_array('render', $args);
}

/**
 * Returns txt output with proper http headers
 *
 * @param string $content_or_func 
 * @param string $layout 
 * @param string $locals 
 * @return string
 */
function txt($content_or_func, $layout = '', $locals = array())
{
  send_header('Content-Type: text/plain; charset='.strtolower(option('encoding')));
  $args = func_get_args();
  return call_user_func_array('render', $args);
}

/**
 * Returns json representation of data with proper http headers.
 * On PHP 5 < PHP 5.2.0, you must provide your own implementation of the
 * <code>json_encode()</code> function beore using <code>json()</code>.
 *
 * @param string $data 
 * @param int $json_option
 * @return string
 */
function json($data, $json_option = 0)
{
  send_header('Content-Type: application/json; charset='.strtolower(option('encoding')));
  return version_compare(PHP_VERSION, '5.3.0', '>=') ? json_encode($data, $json_option) : json_encode($data);
}

/**
 * undocumented function
 *
 * @param string $filename 
 * @param string $return 
 * @return mixed number of bytes delivered or file output if $return = true
 */
function render_file($filename, $return = false)
{
  # TODO implements X-SENDFILE headers
  // if($x-sendfile = option('x-sendfile'))
  // {
  //    // add a X-Sendfile header for apache and Lighttpd >= 1.5
  //    if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header 
  //   
  // }
  // else
  // {
  //   
  // }
  $filename = str_replace('../', '', $filename);
  if(file_exists($filename))
  {
    $content_type = mime_type(file_extension($filename));
    $header = 'Content-type: '.$content_type;
    if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
    send_header($header);
    return file_read($filename, $return);
  }
  else halt(NOT_FOUND, "unknown filename $filename");
}

/**
 * Call before_sending_header() if it exists, then send headers
 * 
 * @param string $header
 * @return void
 */
function send_header($header = null, $replace = true, $code = false)
{
    if(!headers_sent()) 
    {
        call_if_exists('before_sending_header', $header);
        header($header, $replace, $code);
    }
}





                                     # # #




# ============================================================================ #
#    6. HELPERS                                                                #
# ============================================================================ #

/**
 * Returns an url composed of params joined with /
 * A param can be a string or an array.
 * If param is an array, its members will be added at the end of the return url
 * as GET parameters "&key=value".
 *
 * @param string or array $param1, $param2 ... 
 * @return string
 */ 
function url_for($params = null)
{
  $paths  = array();
  $params = func_get_args();
  $GET_params = array();
  foreach($params as $param)
  {
    if(is_array($param))
    {
      $GET_params = array_merge($GET_params, $param);
      continue;
    }
    if(filter_var_url($param))
    {
      $paths[] = $param;
      continue;
    }
    $p = explode('/',$param);
    foreach($p as $v)
    {
      if($v != "") $paths[] = str_replace('%23', '#', rawurlencode($v));
    }
  }

  $path = rtrim(implode('/', $paths), '/');
  
  if(!filter_var_url($path)) 
  {
    # it's a relative URL or an URL without a schema
    $base_uri = option('base_uri');
    $path = file_path($base_uri, $path);
  }
  
  if(!empty($GET_params))
  {
    $is_first_qs_param = true;
    $path_as_no_question_mark = strpos($path, '?') === false;
      
    foreach($GET_params as $k => $v)
    {
      $qs_separator = $is_first_qs_param && $path_as_no_question_mark ? 
                        '?' : '&amp;'; 
      $path .= $qs_separator . rawurlencode($k) . '=' . rawurlencode($v);
      $is_first_qs_param = false;
    }
  }
  
  if(DIRECTORY_SEPARATOR != '/') $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);

  return $path;
}

/**
 * An alias of {@link htmlspecialchars()}.
 * If no $charset is provided, uses option('encoding') value
 *
 * @param string $str 
 * @param string $quote_style 
 * @param string $charset 
 * @return void
 */
function h($str, $quote_style = ENT_NOQUOTES, $charset = null)
{
  if(is_null($charset)) $charset = strtoupper(option('encoding'));
  return htmlspecialchars($str, $quote_style, $charset); 
}

/**
 * Set and returns flash messages that will be available in the next action
 * via the {@link flash_now()} function or the view variable <code>$flash</code>.
 * 
 * If multiple values are provided, set <code>$name</code> variable with an array of those values.
 * If there is only one value, set <code>$name</code> variable with the provided $values
 * or if it's <code>$name</code> is an array, merge it with current messages.
 *
 * @param string, array $name 
 * @param mixed  $values,... 
 * @return mixed variable value for $name if $name argument is provided, else return all variables
 */
function flash($name = null, $value = null)
{
  if(!defined('SID')) trigger_error("Flash messages can't be used because session isn't enabled", E_USER_WARNING);
  static $messages = array();
  $args = func_get_args();
  $name = array_shift($args);
  if(is_null($name)) return $messages;
  if(is_array($name)) return $messages = array_merge($messages, $name);
  if(!empty($args))
  {
    $messages[$name] = count($args) > 1 ? $args : $args[0];
  }
  if(!array_key_exists($name, $messages)) return null;
  else return $messages[$name];
  return $messages;
}

/**
 * Set and returns flash messages available for the current action, included those
 * defined in the previous action with {@link flash()}
 * Those messages will also be passed to the views and made available in the 
 * <code>$flash</code> variable.
 * 
 * If multiple values are provided, set <code>$name</code> variable with an array of those values.
 * If there is only one value, set <code>$name</code> variable with the provided $values
 * or if it's <code>$name</code> is an array, merge it with current messages.
 *
 * @param string, array $name 
 * @param mixed  $values,... 
 * @return mixed variable value for $name if $name argument is provided, else return all variables
 */
function flash_now($name = null, $value = null)
{
  static $messages = null;
  if(is_null($messages))
  {
    $fkey = LIM_SESSION_FLASH_KEY;
    $messages = array();
    if(defined('SID') && array_key_exists($fkey, $_SESSION)) $messages = $_SESSION[$fkey];
  }
  $args = func_get_args();
  $name = array_shift($args);
  if(is_null($name)) return $messages;
  if(is_array($name)) return $messages = array_merge($messages, $name);
  if(!empty($args))
  {
    $messages[$name] = count($args) > 1 ? $args : $args[0];
  }
  if(!array_key_exists($name, $messages)) return null;
  else return $messages[$name];
  return $messages;
}

/**
 * Delete current flash messages in session, and set new ones stored with 
 * flash function.
 * Called before application exit.
 *
 * @access private
 * @return void
 */
function flash_sweep()
{
  if(defined('SID'))
  {
    $fkey = LIM_SESSION_FLASH_KEY;
    $_SESSION[$fkey] = flash();
  }
}

/**
 * Starts capturing block of text
 *
 * Calling without params stops capturing (same as end_content_for()).
 * After capturing the captured block is put into a variable
 * named $name for later use in layouts. If second parameter
 * is supplied, its content will be used instead of capturing
 * a block of text.
 *
 * @param string $name
 * @param string $content
 * @return void
 */
function content_for($name = null, $content = null)
{
  static $_name = null;
  if(is_null($name) && !is_null($_name))
  {
    set($_name, ob_get_clean());
    $_name = null;	
  }
  elseif(!is_null($name) && !isset($content))
  {
    $_name = $name;	
    ob_start();
  }
  elseif(isset($name, $content))
  {
    set($name, $content);
  }
}

/**
 * Stops capturing block of text
 *
 * @return void
 */
function end_content_for()
{
  content_for();
}

/**
 * Shows current memory and execution time of the application.
 * Returns only execution time if <code>memory_get_usage()</code> 
 * isn't available.
 * ( That's the case before PHP5.2.1 if PHP isn't compiled with option 
 *   <code>--enable-memory-limit</code>. )
 * 
 * @access public
 * @return array
 */
function benchmark()
{
	$res = array( 'execution_time' => (microtime() - LIM_START_MICROTIME) );
  if(defined('LIM_START_MEMORY'))
	{
		$current_mem_usage     = memory_get_usage();
		$res['current_memory'] = $current_mem_usage;
		$res['start_memory']   = LIM_START_MEMORY;
		$res['average_memory'] = (LIM_START_MEMORY + $current_mem_usage) / 2;
	}
	
	return $res;
}




                                     # # #




# ============================================================================ #
#    7. UTILS                                                                  #
# ============================================================================ #
 
/**
 * Calls a function if exists
 *
 * @param callback $callback a function stored in a string variable, 
 *   or an object and the name of a method within the object
 *   See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
 *   to learn more about callbacks.
 * @param mixed $arg,.. (optional)
 * @return mixed
 */
function call_if_exists($callback)
{
  $args = func_get_args();
  $callback = array_shift($args);
  if(is_callable($callback)) return call_user_func_array($callback, $args);
  return;
}

/**
 * Define a constant unless it already exists
 *
 * @param string $name 
 * @param string $value 
 * @return void
 */
function define_unless_exists($name, $value)
{
  if(!defined($name)) define($name, $value);
}

/**
 * Return a default value if provided value is empty
 *
 * @param mixed $value 
 * @param mixed $default default value returned if $value is empty
 * @return mixed
 */
function value_or_default($value, $default)
{
  return empty($value) ? $default : $value;
}

/**
 * An alias of {@link value_or_default()}
 *
 * 
 * @param mixed $value 
 * @param mixed $default 
 * @return mixed
 */
function v($value, $default)
{
  return value_or_default($value, $default);
}

/**
 * Load php files with require_once in a given dir
 *
 * @param string $path Path in which are the file to load
 * @param string $pattern a regexp pattern that filter files to load
 * @param bool $prevents_output security option that prevents output
 * @return array paths of loaded files
 */
function require_once_dir($path, $pattern = "*.php", $prevents_output = true)
{
  if($path[strlen($path) - 1] != "/") $path .= "/";
  $filenames = glob($path.$pattern);
  if(!is_array($filenames)) $filenames = array();
  if($prevents_output) ob_start();
  foreach($filenames as $filename) require_once $filename;
  if($prevents_output) ob_end_clean();
  return $filenames;
}

/**
 * Dumps a variable into inspectable format
 *
 * @param anything $var the variable to debug
 * @param bool $output_as_html sets whether to wrap output in <pre> tags. default: true
 * @return string the variable with output
 */
function debug($var, $output_as_html = true)
{ 
  if ( is_null($var) ) { return '<span class="null-value">[NULL]</span>'; };
  $out = '';
  switch ($var) 
  { 
    case empty($var):
      $out = '[empty value]';
      break;
    
    case is_array($var):
      $out = var_export($var, true);
      break;
    
    case is_object($var):
      $out = var_export($var, true);
      break;
      
    case is_string($var):
      $out = $var;
      break;
    
    default:
      $out = var_export($var, true);
      break;
  }
  if ($output_as_html) { $out = "<pre>\n" . h($out) ."</pre>"; }
  return $out;
}


## HTTP utils  _________________________________________________________________


### Constants: HTTP status codes

define( 'HTTP_CONTINUE',                      100 );
define( 'HTTP_SWITCHING_PROTOCOLS',           101 );
define( 'HTTP_PROCESSING',                    102 );
define( 'HTTP_OK',                            200 );
define( 'HTTP_CREATED',                       201 );
define( 'HTTP_ACCEPTED',                      202 );
define( 'HTTP_NON_AUTHORITATIVE',             203 );
define( 'HTTP_NO_CONTENT',                    204 );
define( 'HTTP_RESET_CONTENT',                 205 );
define( 'HTTP_PARTIAL_CONTENT',               206 );
define( 'HTTP_MULTI_STATUS',                  207 );
                                              
define( 'HTTP_MULTIPLE_CHOICES',              300 );
define( 'HTTP_MOVED_PERMANENTLY',             301 );
define( 'HTTP_MOVED_TEMPORARILY',             302 );
define( 'HTTP_SEE_OTHER',                     303 );
define( 'HTTP_NOT_MODIFIED',                  304 );
define( 'HTTP_USE_PROXY',                     305 );
define( 'HTTP_TEMPORARY_REDIRECT',            307 );

define( 'HTTP_BAD_REQUEST',                   400 );
define( 'HTTP_UNAUTHORIZED',                  401 );
define( 'HTTP_PAYMENT_REQUIRED',              402 );
define( 'HTTP_FORBIDDEN',                     403 );
define( 'HTTP_NOT_FOUND',                     404 );
define( 'HTTP_METHOD_NOT_ALLOWED',            405 );
define( 'HTTP_NOT_ACCEPTABLE',                406 );
define( 'HTTP_PROXY_AUTHENTICATION_REQUIRED', 407 );
define( 'HTTP_REQUEST_TIME_OUT',              408 );
define( 'HTTP_CONFLICT',                      409 );
define( 'HTTP_GONE',                          410 );
define( 'HTTP_LENGTH_REQUIRED',               411 );
define( 'HTTP_PRECONDITION_FAILED',           412 );
define( 'HTTP_REQUEST_ENTITY_TOO_LARGE',      413 );
define( 'HTTP_REQUEST_URI_TOO_LARGE',         414 );
define( 'HTTP_UNSUPPORTED_MEDIA_TYPE',        415 );
define( 'HTTP_RANGE_NOT_SATISFIABLE',         416 );
define( 'HTTP_EXPECTATION_FAILED',            417 );
define( 'HTTP_UNPROCESSABLE_ENTITY',          422 );
define( 'HTTP_LOCKED',                        423 );
define( 'HTTP_FAILED_DEPENDENCY',             424 );
define( 'HTTP_UPGRADE_REQUIRED',              426 );

define( 'HTTP_INTERNAL_SERVER_ERROR',         500 );
define( 'HTTP_NOT_IMPLEMENTED',               501 );
define( 'HTTP_BAD_GATEWAY',                   502 );
define( 'HTTP_SERVICE_UNAVAILABLE',           503 );
define( 'HTTP_GATEWAY_TIME_OUT',              504 );
define( 'HTTP_VERSION_NOT_SUPPORTED',         505 );
define( 'HTTP_VARIANT_ALSO_VARIES',           506 );
define( 'HTTP_INSUFFICIENT_STORAGE',          507 );
define( 'HTTP_NOT_EXTENDED',                  510 );

/**
 * Output proper HTTP header for a given HTTP code
 *
 * @param string $code 
 * @return void
 */
function status($code = 500)
{
  if(!headers_sent())
  {
    $str = http_response_status_code($code);
    send_header($str);
  }
}

/**
 * Http redirection
 * 
 * Same use as {@link url_for()}
 * By default HTTP status code is 302, but a different code can be specified
 * with a status key in array parameter.
 * 
 * <code>
 * redirecto('new','url'); # 302 HTTP_MOVED_TEMPORARILY by default
 * redirecto('new','url', array('status' => HTTP_MOVED_PERMANENTLY));
 * </code>
 * 
 * @param string or array $param1, $param2... 
 * @return void
 */
function redirect_to($params)
{
  # [NOTE]: (from php.net) HTTP/1.1 requires an absolute URI as argument to » Location:
  # including the scheme, hostname and absolute path, but some clients accept
  # relative URIs. You can usually use $_SERVER['HTTP_HOST'],
  # $_SERVER['PHP_SELF'] and dirname() to make an absolute URI from a relative
  # one yourself.

  # TODO make absolute uri
  if(!headers_sent())
  {
    $status = HTTP_MOVED_TEMPORARILY; # default for a redirection in PHP
    $params = func_get_args();
    $n_params = array();
    # extract status param if exists
    foreach($params as $param)
    {
      if(is_array($param))
      {
        if(array_key_exists('status', $param))
        {
          $status = $param['status'];
          unset($param['status']);
        }
      }
      $n_params[] = $param;
    }
    $uri = call_user_func_array('url_for', $n_params);
    $uri = htmlspecialchars_decode($uri, ENT_NOQUOTES);
    stop_and_exit(false);
    send_header('Location: '.$uri, true, $status);
    exit;
  }
}

/**
 * Http redirection
 *
 * @deprecated deprecated since version 0.4. Please use {@link redirect_to()} instead.
 * @param string $url 
 * @return void
 */
function redirect($uri)
{
  # halt('redirect() is deprecated. Please use redirect_to() instead.', E_LIM_DEPRECATED);
  # halt not necesary... it won't be visible because of http redirection...
  redirect_to($uri);
}

/**
 * Returns HTTP response status for a given code.
 * If no code provided, return an array of all status
 *
 * @param string $num 
 * @return string,array
 */
function http_response_status($num = null)
{
  $status =  array(
      100 => 'Continue',
      101 => 'Switching Protocols',
      102 => 'Processing',

      200 => 'OK',
      201 => 'Created',
      202 => 'Accepted',
      203 => 'Non-Authoritative Information',
      204 => 'No Content',
      205 => 'Reset Content',
      206 => 'Partial Content',
      207 => 'Multi-Status',
      226 => 'IM Used',

      300 => 'Multiple Choices',
      301 => 'Moved Permanently',
      302 => 'Found',
      303 => 'See Other',
      304 => 'Not Modified',
      305 => 'Use Proxy',
      306 => 'Reserved',
      307 => 'Temporary Redirect',

      400 => 'Bad Request',
      401 => 'Unauthorized',
      402 => 'Payment Required',
      403 => 'Forbidden',
      404 => 'Not Found',
      405 => 'Method Not Allowed',
      406 => 'Not Acceptable',
      407 => 'Proxy Authentication Required',
      408 => 'Request Timeout',
      409 => 'Conflict',
      410 => 'Gone',
      411 => 'Length Required',
      412 => 'Precondition Failed',
      413 => 'Request Entity Too Large',
      414 => 'Request-URI Too Long',
      415 => 'Unsupported Media Type',
      416 => 'Requested Range Not Satisfiable',
      417 => 'Expectation Failed',
      422 => 'Unprocessable Entity',
      423 => 'Locked',
      424 => 'Failed Dependency',
      426 => 'Upgrade Required',

      500 => 'Internal Server Error',
      501 => 'Not Implemented',
      502 => 'Bad Gateway',
      503 => 'Service Unavailable',
      504 => 'Gateway Timeout',
      505 => 'HTTP Version Not Supported',
      506 => 'Variant Also Negotiates',
      507 => 'Insufficient Storage',
      510 => 'Not Extended'
  );
  if(is_null($num)) return $status;
  return array_key_exists($num, $status) ? $status[$num] : '';
}

/**
 * Checks if an HTTP response code is valid
 *
 * @param string $num 
 * @return bool
 */
function http_response_status_is_valid($num)
{
  $r = http_response_status($num);
  return !empty($r);
}

/**
 * Returns an HTTP response status string for a given code
 *
 * @param string $num 
 * @return string
 */
function http_response_status_code($num)
{
  $protocole = empty($_SERVER["SERVER_PROTOCOL"]) ? "HTTP/1.1" : $_SERVER["SERVER_PROTOCOL"];
  if($str = http_response_status($num)) return "$protocole $num $str";
}

/**
 * Check if the _Accept_ header is present, and includes the given `type`.
 *
 * When the _Accept_ header is not present `true` is returned. Otherwise
 * the given `type` is matched by an exact match, and then subtypes. You
 * may pass the subtype such as "html" which is then converted internally
 * to "text/html" using the mime lookup table.
 *
 * @param string $type
 * @param string $env 
 * @return bool
 */
function http_ua_accepts($type, $env = null)
{
  if(is_null($env)) $env = env();
  $accept = array_key_exists('HTTP_ACCEPT', $env['SERVER']) ? $env['SERVER']['HTTP_ACCEPT'] : null;
  
  if(!$accept || $accept === '*/*') return true;
  
  if($type)
  {
    // Allow "html" vs "text/html" etc
    if(!strpos($type, '/')) $type = mime_type($type);
    
    // Check if we have a direct match
    if(strpos($accept, $type) > -1) return true;
    
    // Check if we have type/*  
    $type_parts = explode('/', $type); 
    $type = $type_parts[0].'/*';
    return (strpos($accept, $type) > -1);
  }
  
  return false; 
}

## FILE utils  _________________________________________________________________

/**
 * Returns mime type for a given extension or if no extension is provided,
 * all mime types in an associative array, with extensions as keys. 
 * (extracted from Orbit source http://orbit.luaforge.net/)
 *
 * @param string $ext
 * @return string, array
 */
function mime_type($ext = null)
{
  $types = array(
    'ai'      => 'application/postscript',
    'aif'     => 'audio/x-aiff',
    'aifc'    => 'audio/x-aiff',
    'aiff'    => 'audio/x-aiff',
    'asc'     => 'text/plain',
    'atom'    => 'application/atom+xml',
    'atom'    => 'application/atom+xml',
    'au'      => 'audio/basic',
    'avi'     => 'video/x-msvideo',
    'bcpio'   => 'application/x-bcpio',
    'bin'     => 'application/octet-stream',
    'bmp'     => 'image/bmp',
    'cdf'     => 'application/x-netcdf',
    'cgm'     => 'image/cgm',
    'class'   => 'application/octet-stream',
    'cpio'    => 'application/x-cpio',
    'cpt'     => 'application/mac-compactpro',
    'csh'     => 'application/x-csh',
    'css'     => 'text/css',
    'csv'     => 'text/csv',
    'dcr'     => 'application/x-director',
    'dir'     => 'application/x-director',
    'djv'     => 'image/vnd.djvu',
    'djvu'    => 'image/vnd.djvu',
    'dll'     => 'application/octet-stream',
    'dmg'     => 'application/octet-stream',
    'dms'     => 'application/octet-stream',
    'doc'     => 'application/msword',
    'dtd'     => 'application/xml-dtd',
    'dvi'     => 'application/x-dvi',
    'dxr'     => 'application/x-director',
    'eps'     => 'application/postscript',
    'etx'     => 'text/x-setext',
    'exe'     => 'application/octet-stream',
    'ez'      => 'application/andrew-inset',
    'gif'     => 'image/gif',
    'gram'    => 'application/srgs',
    'grxml'   => 'application/srgs+xml',
    'gtar'    => 'application/x-gtar',
    'hdf'     => 'application/x-hdf',
    'hqx'     => 'application/mac-binhex40',
    'htm'     => 'text/html',
    'html'    => 'text/html',
    'ice'     => 'x-conference/x-cooltalk',
    'ico'     => 'image/x-icon',
    'ics'     => 'text/calendar',
    'ief'     => 'image/ief',
    'ifb'     => 'text/calendar',
    'iges'    => 'model/iges',
    'igs'     => 'model/iges',
    'jpe'     => 'image/jpeg',
    'jpeg'    => 'image/jpeg',
    'jpg'     => 'image/jpeg',
    'js'      => 'application/x-javascript',
    'json'    => 'application/json',
    'kar'     => 'audio/midi',
    'latex'   => 'application/x-latex',
    'lha'     => 'application/octet-stream',
    'lzh'     => 'application/octet-stream',
    'm3u'     => 'audio/x-mpegurl',
    'man'     => 'application/x-troff-man',
    'mathml'  => 'application/mathml+xml',
    'me'      => 'application/x-troff-me',
    'mesh'    => 'model/mesh',
    'mid'     => 'audio/midi',
    'midi'    => 'audio/midi',
    'mif'     => 'application/vnd.mif',
    'mov'     => 'video/quicktime',
    'movie'   => 'video/x-sgi-movie',
    'mp2'     => 'audio/mpeg',
    'mp3'     => 'audio/mpeg',
    'mpe'     => 'video/mpeg',
    'mpeg'    => 'video/mpeg',
    'mpg'     => 'video/mpeg',
    'mpga'    => 'audio/mpeg',
    'ms'      => 'application/x-troff-ms',
    'msh'     => 'model/mesh',
    'mxu'     => 'video/vnd.mpegurl',
    'nc'      => 'application/x-netcdf',
    'oda'     => 'application/oda',
    'ogg'     => 'application/ogg',
    'pbm'     => 'image/x-portable-bitmap',
    'pdb'     => 'chemical/x-pdb',
    'pdf'     => 'application/pdf',
    'pgm'     => 'image/x-portable-graymap',
    'pgn'     => 'application/x-chess-pgn',
    'png'     => 'image/png',
    'pnm'     => 'image/x-portable-anymap',
    'ppm'     => 'image/x-portable-pixmap',
    'ppt'     => 'application/vnd.ms-powerpoint',
    'ps'      => 'application/postscript',
    'qt'      => 'video/quicktime',
    'ra'      => 'audio/x-pn-realaudio',
    'ram'     => 'audio/x-pn-realaudio',
    'ras'     => 'image/x-cmu-raster',
    'rdf'     => 'application/rdf+xml',
    'rgb'     => 'image/x-rgb',
    'rm'      => 'application/vnd.rn-realmedia',
    'roff'    => 'application/x-troff',
    'rss'     => 'application/rss+xml',
    'rtf'     => 'text/rtf',
    'rtx'     => 'text/richtext',
    'sgm'     => 'text/sgml',
    'sgml'    => 'text/sgml',
    'sh'      => 'application/x-sh',
    'shar'    => 'application/x-shar',
    'silo'    => 'model/mesh',
    'sit'     => 'application/x-stuffit',
    'skd'     => 'application/x-koan',
    'skm'     => 'application/x-koan',
    'skp'     => 'application/x-koan',
    'skt'     => 'application/x-koan',
    'smi'     => 'application/smil',
    'smil'    => 'application/smil',
    'snd'     => 'audio/basic',
    'so'      => 'application/octet-stream',
    'spl'     => 'application/x-futuresplash',
    'src'     => 'application/x-wais-source',
    'sv4cpio' => 'application/x-sv4cpio',
    'sv4crc'  => 'application/x-sv4crc',
    'svg'     => 'image/svg+xml',
    'svgz'    => 'image/svg+xml',
    'swf'     => 'application/x-shockwave-flash',
    't'       => 'application/x-troff',
    'tar'     => 'application/x-tar',
    'tcl'     => 'application/x-tcl',
    'tex'     => 'application/x-tex',
    'texi'    => 'application/x-texinfo',
    'texinfo' => 'application/x-texinfo',
    'tif'     => 'image/tiff',
    'tiff'    => 'image/tiff',
    'tr'      => 'application/x-troff',
    'tsv'     => 'text/tab-separated-values',
    'txt'     => 'text/plain',
    'ustar'   => 'application/x-ustar',
    'vcd'     => 'application/x-cdlink',
    'vrml'    => 'model/vrml',
    'vxml'    => 'application/voicexml+xml',
    'wav'     => 'audio/x-wav',
    'wbmp'    => 'image/vnd.wap.wbmp',
    'wbxml'   => 'application/vnd.wap.wbxml',
    'wml'     => 'text/vnd.wap.wml',
    'wmlc'    => 'application/vnd.wap.wmlc',
    'wmls'    => 'text/vnd.wap.wmlscript',
    'wmlsc'   => 'application/vnd.wap.wmlscriptc',
    'wrl'     => 'model/vrml',
    'xbm'     => 'image/x-xbitmap',
    'xht'     => 'application/xhtml+xml',
    'xhtml'   => 'application/xhtml+xml',
    'xls'     => 'application/vnd.ms-excel',
    'xml'     => 'application/xml',
    'xpm'     => 'image/x-xpixmap',
    'xsl'     => 'application/xml',
    'xslt'    => 'application/xslt+xml',
    'xul'     => 'application/vnd.mozilla.xul+xml',
    'xwd'     => 'image/x-xwindowdump',
    'xyz'     => 'chemical/x-xyz',
    'zip'     => 'application/zip'
  );
  return is_null($ext) ? $types : $types[strtolower($ext)];
}

/**
 * Detect MIME Content-type for a file
 *
 * @param string $filename Path to the tested file.
 * @return string
 */
function file_mime_content_type($filename)
{
  $ext = file_extension($filename); /* strtolower isn't necessary */
  if($mime = mime_type($ext)) return $mime;
  elseif (function_exists('finfo_open'))
  {
    if($finfo = finfo_open(FILEINFO_MIME))
    {
      if($mime = finfo_file($finfo, $filename))
      {
        finfo_close($finfo);
        return $mime;        
      }
    }
  }
  return 'application/octet-stream';
}


/**
 * Read and output file content and return filesize in bytes or status after 
 * closing file.
 * This function is very efficient for outputing large files without timeout
 * nor too expensive memory use
 *
 * @param string $filename 
 * @param string $retbytes 
 * @return bool, int
 */
function file_read_chunked($filename, $retbytes = true)
{
  $chunksize = 1*(1024*1024); // how many bytes per chunk
  $buffer    = '';
  $cnt       = 0;
  $handle    = fopen($filename, 'rb');
  if ($handle === false) return false;

  ob_start();
  while (!feof($handle)) {
    $buffer = fread($handle, $chunksize);
    echo $buffer;
    ob_flush();
    flush();
    if ($retbytes) $cnt += strlen($buffer);
    set_time_limit(0);
  }
  ob_end_flush();

  $status = fclose($handle);
  if ($retbytes && $status) return $cnt; // return num. bytes delivered like readfile() does.
  return $status;
}

/**
 * Create a file path by concatenation of given arguments.
 * Windows paths with backslash directory separators are normalized in *nix paths.
 *
 * @param string $path, ... 
 * @return string normalized path
 */
function file_path($path)
{
  $args = func_get_args();
  $ds = '/'; 
  $win_ds = '\\';
  $n_path = count($args) > 1 ? implode($ds, $args) : $path;
  if(strpos($n_path, $win_ds) !== false) $n_path = str_replace( $win_ds, $ds, $n_path );
  $n_path = preg_replace( "#$ds+#", $ds, $n_path);
  
  return $n_path;
}

/**
 * Returns file extension or false if none
 *
 * @param string $filename 
 * @return string, false
 */
function file_extension($filename)
{
  $pos = strrpos($filename, '.');
  if($pos !== false) return substr($filename, $pos + 1);
  return false;
}

/**
 * Checks if $filename is a text file
 *
 * @param string $filename 
 * @return bool
 */
function file_is_text($filename)
{
  if($mime = file_mime_content_type($filename)) return substr($mime,0,5) == "text/";
  return null;
}

/**
 * Checks if $filename is a binary file
 *
 * @param string $filename 
 * @return void
 */
function file_is_binary($filename)
{
  $is_text = file_is_text($filename);
  return is_null($is_text) ? null : !$is_text;
}

/**
 * Return or output file content
 *
 * @return 	string, int
 *				
 **/

function file_read($filename, $return = false)
{
  if(!file_exists($filename)) trigger_error("$filename doesn't exists", E_USER_ERROR);
  if($return) return file_get_contents($filename);
  return file_read_chunked($filename);
}

/**
 * Returns an array of files contained in a directory
 *
 * @param string $dir 
 * @return array
 */
function file_list_dir($dir)
{
  $files = array();
  if ($handle = opendir($dir))
  {
    while (false !== ($file = readdir($handle)))
    {
      if ($file[0] != "." && $file != "..") $files[] = $file;
    }
    closedir($handle);
  }
  return $files;
}

## Extra utils  ________________________________________________________________

if(!function_exists('array_replace'))
{
  /**
   * For PHP 5 < 5.3.0 (backward compatibility)
   * (from {@link http://www.php.net/manual/fr/function.array-replace.php#92549 this php doc. note})
   * 
   * @see array_replace()
   * @param string $array 
   * @param string $array1 
   * @return $array
   */
  function array_replace( array &$array, array &$array1 )
  {
    $args  = func_get_args();
    $count = func_num_args();

    for ($i = 0; $i < $count; ++$i)
    {
      if(is_array($args[$i]))
      {
        foreach ($args[$i] as $key => $val) $array[$key] = $val;
      }
      else
      {
        trigger_error(
          __FUNCTION__ . '(): Argument #' . ($i+1) . ' is not an array',
          E_USER_WARNING
        );
        return null;
      }
    }
    return $array;
  }
}

/**
 * Check if a string is an url
 *
 * This implementation no longer requires 
 * {@link http://www.php.net/manual/en/book.filter.php the filter extenstion}, 
 * so it will improve compatibility with older PHP versions.
 *
 * @param string $str 
 * @return false, str   the string if true, false instead
 */
function filter_var_url($str)
{
  $regexp = '@^https?://([-[:alnum:]]+\.)+[a-zA-Z]{2,6}(:[0-9]+)?(.*)?$@';
  $options = array( "options" => array("regexp" => $regexp ));
  return preg_match($regexp, $str) ? $str : false;
}


/**
 * For PHP 5 < 5.1.0 (backward compatibility)
 * (from {@link http://www.php.net/manual/en/function.htmlspecialchars-decode.php#82133})
 * 
 * @param string $string 
 * @param string $quote_style, one of: ENT_COMPAT, ENT_QUOTES, ENT_NOQUOTES 
 * @return the decoded string
 */
function limonade_htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
{
	$table = array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style));
	if($quote_style === ENT_QUOTES)
		$table['&#039;'] = $table['&#39;'] = '\'';
	return strtr($string, $table);
}

if(!function_exists('htmlspecialchars_decode'))
{
	function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
	{
		return limonade_htmlspecialchars_decode($string, $quote_style);
	}
}

/**
 * Called just after loading libs, it provides fallback for some 
 * functions if they don't exists.
 *
 */
function fallbacks_for_not_implemented_functions()
{
  if(!function_exists('json_encode'))
  {
    /**
     * for PHP 5 < PHP 5.2.0
     *
     */
    function json_encode()
    {
      trigger_error(
        __FUNCTION__ . '(): no JSON functions available. Please provide your own implementation of ' . __FUNCTION__ . '() in order to use it.', E_USER_WARNING
      );
    }
  }
}



#   ================================= END ==================================   #
<?php
/**
 * Abstract methods that might be redefined by user
 * Do not include this file in your app: it only aims to provide documentation
 * about those functions.
 * 
 * @package limonade
 * @subpackage abstract
 */
 
/**
 * It will be called when app is launched (at the begining of the run function).
 * You can define options inside it, a connection to a database ...
 *
 * @abstract this function might be redefined by user
 * @return void 
 */
function configure()
{
  return;
}

/**
 * Called in run() just after session start, and before checking request method
 * and output buffer start.  
 *
 * @abstract this function might be redefined by user
 * @return void 
 */
function initialize()
{
  return;
}

/**
 * Called in run() just after the route matching, in order to load controllers. 
 * If not specfied, the default function is called:
 * 
 * <code>
 * function autoload_controller($callback)
 * {
 *   require_once_dir(option('controllers_dir'));
 * }
 * </code>
 * 
 *
 * @param string $callback the callback defined in matching route
 * @return void
 */
function autoload_controller($callback)
{
  return;
}
 
/**
 * Called before each request.
 * This is very useful to define a default layout or passing common variables
 * to the templates.
 *
 * @abstract this function might be redefined by user
 * @param array() $route array (like returned by {@link route_build()},
 *   with keys "method", "pattern", "names", "callback", "options")
 * @return void 
 */
function before($route)
{
  
}
 
/**
 * An `after` output filter
 * 
 * Called after each request and can apply a transformation to the output
 * (except for `render_file` outputs  which are sent directly to the output buffer).
 *
 * @abstract this function might be redefined by user
 * @param string $output 
 * @param array() $route array (like returned by {@link route_find()},
 *   with keys "method", "pattern", "names", "callback", "params", "options")
 * @return string 
 */
function after($output, $route)
{
  # Call functions...
  # .. modifies $output...
  return $output;
}
 
/**
 * Not found error output
 *
 * @abstract this function might be redefined by user
 * @param string $errno 
 * @param string $errstr 
 * @param string $errfile 
 * @param string $errline 
 * @return string "not found" output string
 */
function not_found($errno, $errstr, $errfile=null, $errline=null)
{
 
}
 
/**
 * Server error output
 *
 * @abstract this function might be redefined by user
 * @param string $errno 
 * @param string $errstr 
 * @param string $errfile 
 * @param string $errline 
 * @return string "server error" output string
 */
function server_error($errno, $errstr, $errfile=null, $errline=null)
{
  
}
 
/**
 * Called when a route is not found.
 * 
 * 
 * @abstract this function might be redefined by user
 * @param string $request_method 
 * @param string $request_uri 
 * @return void 
 */
function route_missing($request_method, $request_uri)
{
  halt(NOT_FOUND, "($request_method) $request_uri"); # by default
}

/**
 * Called before stoppping and exiting application.
 *
 * @abstract this function might be redefined by user
 * @param boolean exit or not
 * @return void 
 */
function before_exit($exit)
{
  
}

/**
 * Rendering prefilter.
 * Useful if you want to transform your views before rendering.
 * The first three parameters are the same as those provided 
 * to the `render` function.
 *
 * @abstract this function might be redefined by user
 * @param string $content_or_func a function, a file in current views dir or a string
 * @param string $layout 
 * @param array $locals 
 * @param array $view_path (by default <code>file_path(option('views_dir'),$content_or_func);</code>)
 * @return array with, in order, $content_or_func, $layout, $locals vars
 *  and the calculated $view_path
 */
function before_render($content_or_func, $layout, $locals, $view_path)
{
  # transform $content_or_func, $layout, $locals or $view_path…
  return array($content_or_func, $layout, $locals, $view_path);
}


/**
 * Called only if rendering $output is_null,
 * like in a controller with no return statement.
 *
 * @abstract this function might be defined by user
 * @param array() $route array (like returned by {@link route_build()},
 *   with keys "method", "pattern", "names", "callback", "options")
 * @return string
 */
function autorender($route)
{
  # process output depending on $route
  return $output;
}

/**
 * Called if a header is about to be sent
 *
 * @abstract this function might be defined by user
 * @param string the headers that limonade will send
 * @return void
 */
function before_sending_header($header)
{

}


<?php
/**
 * @package tests
 * @subpackage assertions
 */
# ============================================================================ #
#    ASSERTIONS                                                                #
# ============================================================================ #

/**
 * assert_true
 *
 * @param string $value 
 * @param string $message 
 * @return boolean
 */
function assert_true($value, $message = '<1> should be TRUE')
{
   test_run_assertion();
   return assert('$value === TRUE; //'.$message);
}

function assert_false($value, $message = '<1> should be FALSE')
{
   test_run_assertion();
   return assert('$value === FALSE; //'.$message);
}

function assert_null($value, $message = '<1> should be NULL')
{
   test_run_assertion();
   return assert('$value === NULL; //'.$message);
}

function assert_not_null($value, $message = '<1> should not be NULL')
{
   test_run_assertion();
   return assert('$value !== NULL; //'.$message);
}

function assert_empty($value, $message = '<1> should be empty')
{
   test_run_assertion();
   return assert('empty($value); //'.$message);
}

function assert_not_empty($value, $message = '<1> should not be empty')
{
   test_run_assertion();
   return assert('!empty($value); //'.$message);
}

function assert_equal($expected, $value, $message = '<1> should be equal to <2>')
{
   test_run_assertion();
   return assert('$expected == $value; //'.$message);
}

function assert_not_equal($expected, $value, $message = '<1> should not equal to <2>')
{
   test_run_assertion();
   return assert('$expected != $value; //'.$message);
}

function assert_identical($expected, $value, $message = '<1> should be identical to <2>')
{
   test_run_assertion();
   return assert('$expected === $value; //'.$message);
}

function assert_not_identical($expected, $value, $message = '<1> should not be identical to <2>')
{
   test_run_assertion();
   return assert('$expected !== $value; //'.$message);
}

function assert_match($pattern, $string, $message = '<2> expected to match regular expression <1>') {
   test_run_assertion();
   return assert('preg_match($pattern, $string); //'.$message);
}
 
function assert_no_match($pattern, $string, $message = '<2> expected to not match regular expression <1>') {
   test_run_assertion();
   return assert('!preg_match($pattern, $string); //'.$message);
}

function assert_type($type, $value, $message = '<1> is not of type <2>') {
  test_run_assertion();
  $predicate = 'is_' . strtolower(is_string($type) ? $type : gettype($type));
  return assert('$predicate($value); //'.$message);
}
 
function assert_instance_of($class, $object, $message = '<2> is not an instance of class <1>') {
   test_run_assertion();
   return assert('$object instanceof $class; //'.$message);
}
 
function assert_length_of($value, $length, $message = '<1> expected to be of length <2>') {
   test_run_assertion();
   $count = is_string($value) ? 'strlen' : 'count';
   return assert('$count($value) == $length; //'.$message);
}

function assert_trigger_error($callable, $args = array(), $message = '<1> should trigger an error') {
  test_run_assertion();
  $trigger_errors = count($GLOBALS["limonade"]["test_errors"]);
  set_error_handler("test_error_handler");
  $result = call_user_func_array($callable, $args);
  restore_error_handler();
  return assert('$trigger_errors < count($GLOBALS["limonade"]["test_errors"]); //'.$message);
}

# TODO add web browser assertions assert_http_get, assert_http_response... as in SimpleTest (http://www.simpletest.org/en/web_tester_documentation.html)

function assert_header($response, $expected_name, $expected_value = null, $message = "expected header '%s' to be equal to '%s' but received '%s: %s'")
{
  test_run_assertion();
  # see assert_header in http://github.com/fnando/voodoo-test/blob/f3b0994ef138a6ba94d5e7cef6c1fb1720797a86/lib/assertions.php
  $headers = preg_split("/^\s*$/ms", $response);
  //var_dump($headers);    
  $headers = preg_replace("/\s*$/sm", "", $headers[0]);
  //var_dump($headers);   
  
  $regex_header = str_replace("/", "\\/", $expected_name);
  $regex_header = str_replace(".", "\\.", $regex_header);
  
  $header = $expected_name;
  
  # from http://www.faqs.org/rfcs/rfc2616
  # Field names are case-insensitive
  if ($expected_value) {
      $regex = "/^{$regex_header}:(.*?)$/ism";
      $header .= ": {$expected_value}";
  } else {
      $regex = "/^{$regex_header}(:.*?)?$/ism";
  }
  
  $has_header = preg_match($regex, $headers, $matches);    
  $sent_header = trim((string)$matches[1]);

  
  if(empty($sent_header))
  {
    if(is_null($expected_value))
    {
      $message = "expected header '%s' but header has not been sent";
    }
    else
    {
      $message = "expected header '%s' to be equal to '%s' but header has not been sent";
    }
    
    $message = sprintf($message, $expected_name, $expected_value);
    return assert("false; //".$message);
  }
  else if($expected_value)
  {
    $message = sprintf($message, $expected_name, $expected_value, $expected_name, $sent_header);
    return assert('$expected_value && $sent_header == $expected_value; //'.$message);
  }
  return assert("true; //");
}

function assert_status($response, $expected_status, $message = "expected status code to be equal to '%s' but received '%s'")
{
  $lines = explode('\n', trim($response));
  if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i', $lines[0], $matches))
  {
      $status = $matches[2];
      return assert('$expected_status == $status; //'.sprintf($message, $expected_status, $status));
  }
  return assert("false; //no status code returned in this response string");
}
<?php
/**
 * A quick little function to interact with a MySQL database.
 * https://gist.github.com/AngeloR/919695
 * 
 * When working with Limonade-php a full-fledged MySQL wrapper seems like
 * overkill. This method instead accepts any mysql statement and if it works
 * returns either the result or the number of rows affected. If neither worked,
 * then it returns false
 *
 * @param   string      $sql    the sql statement you want to execute
 * @param   resource    $c      mysql connect link identifier, if multi-connect
 *                              otheriwse, you can leave it blank
 * @return  MIXED       array   the result set if the sql statement was a SELECT
 *                      integer if the sql statement was INSERT|UPDATE|DELETE
 *                      bool    if anything went wrong with executing your statement
 *
 * Usage:
 * 
 * $c = mysqli_connect(MYSQL_SERVER, MYSQL_USER, MYSQL_PASS, MYSQL_DATABASE);
 * 
 * [update|insert|delete]
 * 
 * if(db('update mytable set myrow = 4 where someotherrow = 3') !== false) {
 *  // worked!
 * }
 *
 * [select]
 * $res = db('select * from mytable');
 */
function db($sql,$c = null) {
	$res = false;
	$q = ( $c === null ) ? @mysqli_query( "SET NAMES 'utf8';" ) : @mysqli_query( $c, "SET NAMES 'utf8';" );
	$q = ($c === null)?@mysqli_query($sql):@mysqli_query($c,$sql);

	if($q) {
		if(strpos(strtolower($sql),'select') === 0) {
			$res = array();
			while($r = mysqli_fetch_assoc($q)) {
				$res[] = $r;
			}
		} else {
			$res = ($c === null)?mysqli_affected_rows():mysqli_affected_rows($c);
		}
	}
	return $res;
}
<?php
/**
 * @package tests
 */
 
# ============================================================================ #
#    TESTS                                                                     #
# ============================================================================ #

/**
 * load assertions
 */
require_once dirname(__FILE__)."/assertions.php";


 
/**
 * Constants and globals
 */
if(!defined('DS')) define("DS", DIRECTORY_SEPARATOR);

if(!array_key_exists("limonade", $GLOBALS))
   $GLOBALS["limonade"] = array();
if(!array_key_exists("test_cases", $GLOBALS["limonade"]))
   $GLOBALS["limonade"]["test_cases"] = array();
if(!array_key_exists("test_errors", $GLOBALS["limonade"]))
   $GLOBALS["limonade"]["test_errors"] = array();
if(!array_key_exists("test_case_current", $GLOBALS["limonade"]))
   $GLOBALS["limonade"]["test_case_current"] = NULL;
if(!array_key_exists("test_suites", $GLOBALS["limonade"]))
   $GLOBALS["limonade"]["test_suites"] = NULL;

ini_set("display_errors", true);
error_reporting(E_ALL ^ (E_USER_WARNING | E_NOTICE | E_USER_NOTICE));
// error_reporting(E_ALL | E_STRICT);
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_BAIL, 0);
assert_options(ASSERT_QUIET_EVAL, 0);
assert_options(ASSERT_CALLBACK, 'test_assert_failure');

# TODO: separate display from logic
# TODO: clean results output
# TODO: add all tests results

/**
 * Starts a test suite
 *
 * @param string $name 
 * @return void
 */
function test_suite($name)
{
  $GLOBALS["limonade"]["test_suites"] = $name;
  echo test_cli_format("===========================================================\n", 'white');
  echo test_cli_format(">>>> START $name tests suites\n", 'white');
  echo test_cli_format("-----------------------------------------------------------\n", 'white');
}

/**
 * Ends the last group of test suites
 *
 * @return void
 */
function end_test_suite()
{
  $name         = $GLOBALS["limonade"]["test_suites"];
  $failures     = 0;
  $tests        = 0;
  $passed_tests = 0;
  $assertions   = 0;

  foreach($GLOBALS["limonade"]["test_cases"] as $test)
  {
    $failures += $test['failures'];
    $assertions += $test['assertions'];
    if(empty($test['failures'])) $passed_tests++;
    $tests++;
  }
  echo ">> ENDING $name tests suites\n  ";
  echo $failures > 0 ? test_cli_format("|FAILED!|", "red") : test_cli_format("|PASSED|", "green");;
  echo " Passes ".$passed_tests."/".$tests.", ";
  echo " {$failures} failures for {$assertions} assertions.\n";
  echo test_cli_format("===========================================================\n", 'white');
}

/**
 * Starting a new test case
 *
 * @param string $name 
 * @return void
 */
function test_case($name)
{
   $name = strtolower($name); // TODO: normalize name
   
   if(!array_key_exists($name, $GLOBALS["limonade"]["test_cases"]))
   {
      $GLOBALS["limonade"]["test_cases"][$name] = array( 
                                                      "name" => $name,
                                                      "assertions" => 0, 
                                                      "failures" => 0,
                                                      "description" => NULL
                                                   );      
      $GLOBALS["limonade"]["test_case_current"] = $name;
   }
   else
   {
      
   }
}

/**
 * Displays and ending the current tests suite
 * 
 * @return void
 */
function end_test_case()
{
   $name = $GLOBALS["limonade"]["test_case_current"];
   echo "## ".strtoupper($name)."\n";
      
   $desc = test_case_describe();
   if(!is_null($desc)) echo $desc."\n";

   echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n";
   
   test_case_execute_current();
   
   
   if(!is_null($name))
   {
      $test = $GLOBALS["limonade"]["test_cases"][$name];
      // closing previous test
      echo "\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n";
      echo $test['failures'] > 0 ? test_cli_format("|FAILED!|", "red") : test_cli_format("|PASSED|", "green");
      echo " Test case '$name' finished: ";
      echo count(test_case_all_func())." tests, ";
      echo " {$test['failures']} failures for {$test['assertions']} assertions.\n";
      
      echo "-----------------------------------------------------------\n";
   }
   $GLOBALS["limonade"]["test_case_current"] = null;
}

/**
 * Describes the current tests suite
 *
 * @param string $msg 
 * @return string tests description
 */
function test_case_describe($msg = NULL)
{
   $test =& test_case_current();
   if(!is_null($msg))
   {
      $test["description"] = $msg;
   }
   //var_dump($test["description"]);
   return $test["description"];
}

/**
 * Returns all user test case functions
 *
 * @access private
 * @return void
 */
function test_case_all_func()
{
   $functions = get_defined_functions();
   $functions = $functions['user'];
   $tests = array();
   $name = $GLOBALS["limonade"]["test_case_current"];
   while ($func = array_shift($functions)) {
      $regexp = "/^test_{$name}_(.*)$/";
      if(!preg_match($regexp, $func)) continue;
      if($func == test_before_func_name()) continue;
      // TODO: adding break for all test api methods
      
      $tests[] = $func;
   }
   return $tests;
}

/**
 * Execute current test case
 *
 * @access private
 * @return void
 */
function test_case_execute_current()
{
   $tests = test_case_all_func();
   while($func = array_shift($tests))
   {
      test_call_func(test_before_func_name());
      call_user_func($func);
   }
}


function &test_case_current()
{
   $name = $GLOBALS["limonade"]["test_case_current"];
   return $GLOBALS["limonade"]["test_cases"][$name];
}



function test_before_func_name()
{
   $test = test_case_current();
   $func = "before_each_test_in_".$test["name"];
   return $func;
}

function test_before_assert_func_name()
{
   $test = test_case_current();
   $func = "before_each_assert_in_$name".$test["name"];
   return $func;
}

function test_run_assertion()
{
   $name = $GLOBALS["limonade"]["test_case_current"];
   $GLOBALS["limonade"]["test_cases"][$name]['assertions']++;
   test_call_func(test_before_assert_func_name());
}

/**
 * Calls a function if exists
 *
 * @param string $func the function name
 * @param mixed $arg,.. (optional)
 * @return mixed
 */
function test_call_func($func)
{
  if(empty($func)) return;
  $args = func_get_args();
  $func = array_shift($args);
  if(function_exists($func)) return call_user_func_array($func, $args);
  return;
}

/**
 * Error handler 
 * 
 * @access private
 * @return boolean true
 */
function test_error_handler($errno, $errstr, $errfile, $errline)
{
	if($errno < E_USER_ERROR || $errno > E_USER_NOTICE) 
	   echo test_cli_format("!!! ERROR", "red") . " [$errno], $errstr in $errfile at line $errline\n";
	$GLOBALS["limonade"]["test_errors"][] = array($errno, $errstr, $errfile, $errline);
   return true;
}

/**
 * Assert callback
 * 
 * @access private
 * @param string $script 
 * @param string $line 
 * @param string $message 
 * @return void
 */
function test_assert_failure($script, $line, $message)
{
   // Using the stack trace, find the outermost assert*() call
   $stacktrace = array_slice(debug_backtrace(), 1); // skip self
   $assertion = reset($stacktrace);
   while ($stackframe = array_shift($stacktrace)) {
    if (!preg_match('/^assert/', $stackframe['function']))
      break;
    $assertion = $stackframe;
   }

   extract($assertion, EXTR_PREFIX_ALL, 'assert');
   $code = explode("\n", file_get_contents($assert_file));
   $code = trim($code[$assert_line - 1]);
   
   list($assert_code, $message) = explode("//", $message);
   echo test_cli_format("Assertion failed", "yellow");
   echo " in script *{$assert_file}* (line {$assert_line}):\n";
   echo "   * assertion: $code\n";
   echo "   * message:   $message\n";
   $name = $GLOBALS["limonade"]["test_case_current"];
   $GLOBALS["limonade"]["test_cases"][$name]['failures']++;
}

function test_cli_format($text, $format) {
    $formats = array(
        "blue"       => 34,
        "bold"       => 1,
        "green"      => 32,
        "highlight"  => 7,
        "light_blue" => 36,
        "purple"     => 35,
        "red"        => 31,
        "underline"  => 4,
        "white"      => 37,
        "yellow"     => 33
    );

    if (array_key_exists($format, $formats)) $format = $formats[$format];
    return chr(27) . "[01;{$format} m{$text}" . chr(27) . "[00m";
}

/**
 * Do HTTP request and return the response content.
 * 
 * @param string $url
 * @param string $method
 * @param bool $include_header
 * @return string
 * @author Nando Vieira
 */
function test_request($url, $method="GET", $include_header=false, $post_data=array(), $http_header=array()) {
    $method = strtoupper($method);
    $allowed_methods = array("GET", "PUT", "POST", "DELETE", "HEAD");
    if(!in_array($method, $allowed_methods))
    {
      $message = "The requested method '$method' is not allowed";
      return assert('false; //'.$message);
    }
    
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_HEADER, $include_header);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $http_header);
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); 
    if($method == 'POST' || $method == 'PUT')
    {
      curl_setopt($curl, CURLOPT_POST, 1);
      curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
    }
    if($method == 'HEAD')
    {
      curl_setopt($curl, CURLOPT_NOBODY, true);
    }
    $response = curl_exec($curl);
    curl_close($curl);

    return $response;
}
<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title><?=$sitename?></title>
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta name='robots' content='noindex' />
	<link rel="stylesheet" href="css/bootstrap.css">
	<link rel="stylesheet" href="css/style.css?v=<?=$app_version?>">
	<link rel="shortcut icon" href="favicon.png?v=<?=$app_version?>" />
</head>
<body>

	<!-- HEADER -->

	<div class="navbar navbar-fixed-top">
		<div class="navbar-inner">
			<div class="container-fluid">
				<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
					<span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>
				</button>
				<a class="brand" href="?"><?=$sitename?></a>
				<div class="nav-collapse collapse">
					<p class="navbar-text pull-right">
						<strong title="<?=$lang['loggedinas']?> <?=$username?>"><?=$username?></strong> <a href="logout" class="navbar-link" title="<?=$lang['logout']?>"><i class="icon-lock icon-white"></i></a>
					</p>
					<ul class="nav">
						<li>
							<form class="navbar-form">
								<a href="#top" class="btn" title="<?=$lang['viewtable']?>"><i class="icon-th icon-white"></i></a>
								<a href="#main" class="btn" title="<?=$lang['viewdetails']?>"><i class="icon-user icon-white"></i></a>
								<a href="#main" class="btn btn-primary clearform" title="<?=$lang['contactcreate']?>"><i class="icon-plus"></i></a>
								<input id="s" class="input-large search-query" type="text" placeholder="<?=$lang['searchplaceholder']?>&hellip;">
							</form>
						</li>
						<li class="divider-vertical"></li>
						<li><a href="#top" title="<?=$lang['showall']?>" class="refresh"><i class="icon-refresh icon-white"></i></a></li>
					</ul>
				</div><!--/.nav-collapse -->
			</div>
		</div>
	</div>


	<!-- CONTAINER -->

	<div id="table" class="container-fluid">
		<div class="row-fluid">

			<div class="span12">
				<table id="people-table" class="table table-striped table-hover table-condensed">
					<thead>
						<tr><th class="active" data-name="name"><?=$lang['name']?></th><th data-name="title"><?=$lang['title']?></th></tr>
					</thead>
					<tbody></tbody>
				</table>
			</div>

		</div>
	</div>


	<hr>


	<!-- CONTAINER -->

	<div id="main" class="container-fluid">
		<div class="row-fluid">

			<!-- MAIN -->

			<div class="span12">

				<div class="row-fluid">
					<div class="span10">
						<input type="text" id="name" placeholder="<?=$lang['name']?>">
						<input type="hidden" id="id">
						<p class="lead"><input type="text" id="title" placeholder="<?=$lang['title']?>"></p>
					</div>
					<div class="span2">
						<p class="pull-right"><a class="save btn btn-success hide" href="#"><?=$lang['contactsave']?></a></p>
					</div>
				</div>


				<div class="row-fluid">
					<div class="span6">

						<div id="form">
							<!-- form fields go here -->
						</div>

						<p>
							<a href="#" class="save btn btn-success hide"><?=$lang['contactsave']?></a>
							<a href="#" id="delete" class="pull-right btn btn-mini btn-danger hide"><?=$lang['contactdelete']?></a>
						</p>

					</div><!--/span-->
					<div class="span6">

						<div id="commentbox" class="hide">
							<label><?=$lang['comments']?></label>

							<textarea id="c" class="span12" rows="3"></textarea>
							<p><a href="#" id="c_button" class="btn pull-right"><?=$lang['commentadd']?></a></p>
						</div>

						<div id="comments">
							<!-- comments go here -->
						</div>


					</div><!--/span-->
				</div><!--/row-->

			</div><!--/span-->
		</div><!--/row-->


		<div id="notification" class="alert hide" title="<?=$lang['clicktoclose']?>"></div>


		<footer>

			<hr>

			<div class="row-fluid">
				<div class="span6">
					<p><small>&copy; <?=date('Y')?> <a href="https://github.com/luckyshot/crmx" target="_blank">CRMx</a> <?=$app_version?> <?=$lang['by']?> <a href="http://xaviesteve.com" target="_blank">Xavi</a></small></p>
				</div>
				<div class="span6">
					<p class="tr"><a href="#top"><?=$lang['backtotop']?></a></p>
				</div>
			</div>
		</footer>

	</div><!--/.fluid-container-->

	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<!--	<script src="js/script.js?v=<?=$app_version?>"></script>
-->
	<script src="js/bootstrap-collapse.js"></script>
	<script src="js/bootstrap-dropdown.js"></script>
	<script src="js/jquery-easydate.js"></script>
	<script src="js/crmx.js"></script>

	<script>
		$(document).ready(function(){
			// Hard-code from backend
			crmx.config = {
				sitename: '<?=$sitename?>',
				username: '<?=$username?>',
				plugins: <?=$plugins?>,
				lang: <?=json_encode($lang)?>

			};
			crmx.form = <?=$form?>;
			crmx.people = <?=$people?>;
			// Start the app
			crmx.run();
		});
	</script>

</body>
</html>