Welcome to the world, October :-)

This commit is contained in:
Sam Georges 2014-05-14 23:24:20 +10:00
parent d412ea6855
commit 71a5dd67ab
1009 changed files with 322066 additions and 0 deletions

182
app/config/app.php Normal file
View File

@ -0,0 +1,182 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => true,
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| your application so that it is used when running Artisan tasks.
|
*/
'url' => 'http://localhost',
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'en',
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is used by the Illuminate encrypter service and should be set
| to a random, 32 character string, otherwise these encrypted strings
| will not be safe. Please do this before deploying an application!
|
*/
'key' => 'Rocktober!!!',
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
|
| The service providers listed here will be automatically loaded on the
| request to your application. Feel free to add your own services to
| this array to grant expanded functionality to your applications.
|
*/
'providers' => array_merge(include(base_path().'/modules/system/providers.php'), array(
//* 'Illuminate\Foundation\Providers\ArtisanServiceProvider',
// 'Illuminate\Auth\AuthServiceProvider', // October has it's own authentication service
//* 'Illuminate\Cache\CacheServiceProvider',
//* 'Illuminate\Session\CommandsServiceProvider',
//* 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider',
//* 'Illuminate\Routing\ControllerServiceProvider',
//* 'Illuminate\Cookie\CookieServiceProvider',
//* 'Illuminate\Database\DatabaseServiceProvider',
//* 'Illuminate\Encryption\EncryptionServiceProvider',
//* 'October\Rain\Filesystem\FilesystemServiceProvider',
// 'Illuminate\Filesystem\FilesystemServiceProvider', // October has it's own Filesystem service
//* 'Illuminate\Hashing\HashServiceProvider',
// 'Illuminate\Html\HtmlServiceProvider', // October has it's own Html service
//* 'Illuminate\Log\LogServiceProvider',
//* 'Illuminate\Mail\MailServiceProvider',
//* 'Illuminate\Database\MigrationServiceProvider',
//* 'Illuminate\Foundation\Providers\OptimizeServiceProvider',
//* 'Illuminate\Pagination\PaginationServiceProvider',
//* 'Illuminate\Queue\QueueServiceProvider',
// 'Illuminate\Redis\RedisServiceProvider',
//* 'Illuminate\Remote\RemoteServiceProvider',
// 'Illuminate\Auth\Reminders\ReminderServiceProvider',
//* 'Illuminate\Database\SeedServiceProvider',
//* 'Illuminate\Foundation\Providers\ServerServiceProvider',
//* 'Illuminate\Session\SessionServiceProvider',
// 'Illuminate\Translation\TranslationServiceProvider', // October has it's own translation service
//* 'Illuminate\Validation\ValidationServiceProvider',
// 'Illuminate\View\ViewServiceProvider',
// 'Illuminate\Workbench\WorkbenchServiceProvider',
'System\ServiceProvider',
)),
/*
|--------------------------------------------------------------------------
| Service Provider Manifest
|--------------------------------------------------------------------------
|
| The service provider manifest is used by Laravel to lazy load service
| providers which are not needed for each request, as well to keep a
| list of all of the services. Here, you may set its storage spot.
|
*/
'manifest' => storage_path().'/meta',
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
|
| This array of class aliases will be registered when this application
| is started. However, feel free to register as many as you wish as
| the aliases are "lazy" loaded so they don't hinder performance.
|
*/
'aliases' => array_merge(include(base_path().'/modules/system/aliases.php'), array(
//* 'App' => 'Illuminate\Support\Facades\App',
//* 'Artisan' => 'Illuminate\Support\Facades\Artisan',
// 'Auth' => 'Illuminate\Support\Facades\Auth',
// 'Blade' => 'Illuminate\Support\Facades\Blade',
//* 'Cache' => 'Illuminate\Support\Facades\Cache',
//* 'ClassLoader' => 'Illuminate\Support\ClassLoader',
//* 'Config' => 'Illuminate\Support\Facades\Config',
//* 'Controller' => 'Illuminate\Routing\Controller',
//* 'Cookie' => 'Illuminate\Support\Facades\Cookie',
//* 'Crypt' => 'Illuminate\Support\Facades\Crypt',
//* 'DB' => 'Illuminate\Support\Facades\DB',
// 'Eloquent' => 'Illuminate\Database\Eloquent\Model',
//* 'Event' => 'Illuminate\Support\Facades\Event',
// 'File' => 'Illuminate\Support\Facades\File',
// 'Form' => 'Illuminate\Support\Facades\Form',
//* 'Hash' => 'Illuminate\Support\Facades\Hash',
//* 'HTML' => 'Illuminate\Support\Facades\HTML',
//* 'Input' => 'Illuminate\Support\Facades\Input',
//* 'Lang' => 'Illuminate\Support\Facades\Lang',
//* 'Log' => 'Illuminate\Support\Facades\Log',
//* 'Mail' => 'Illuminate\Support\Facades\Mail',
//* 'Paginator' => 'Illuminate\Support\Facades\Paginator',
//* 'Password' => 'Illuminate\Support\Facades\Password',
//* 'Queue' => 'Illuminate\Support\Facades\Queue',
//* 'Redirect' => 'Illuminate\Support\Facades\Redirect',
//* 'Redis' => 'Illuminate\Support\Facades\Redis',
//* 'Request' => 'Illuminate\Support\Facades\Request',
//* 'Response' => 'Illuminate\Support\Facades\Response',
//* 'Route' => 'Illuminate\Support\Facades\Route',
//* 'Schema' => 'Illuminate\Support\Facades\Schema',
// 'Seeder' => 'Illuminate\Database\Seeder',
//* 'Session' => 'Illuminate\Support\Facades\Session',
// 'SSH' => 'Illuminate\Support\Facades\SSH',
// 'Str' => 'Illuminate\Support\Str',
//* 'URL' => 'Illuminate\Support\Facades\URL',
//* 'Validator' => 'Illuminate\Support\Facades\Validator',
//* 'View' => 'Illuminate\Support\Facades\View',
)),
);

89
app/config/cache.php Normal file
View File

@ -0,0 +1,89 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Default Cache Driver
|--------------------------------------------------------------------------
|
| This option controls the default cache "driver" that will be used when
| using the Caching library. Of course, you may use other drivers any
| time you wish. This is the default when another is not specified.
|
| Supported: "file", "database", "apc", "memcached", "redis", "array"
|
*/
'driver' => 'file',
/*
|--------------------------------------------------------------------------
| File Cache Location
|--------------------------------------------------------------------------
|
| When using the "file" cache driver, we need a location where the cache
| files may be stored. A sensible default has been specified, but you
| are free to change it to any other place on disk that you desire.
|
*/
'path' => storage_path().'/cache',
/*
|--------------------------------------------------------------------------
| Database Cache Connection
|--------------------------------------------------------------------------
|
| When using the "database" cache driver you may specify the connection
| that should be used to store the cached items. When this option is
| null the default database connection will be utilized for cache.
|
*/
'connection' => null,
/*
|--------------------------------------------------------------------------
| Database Cache Table
|--------------------------------------------------------------------------
|
| When using the "database" cache driver we need to know the table that
| should be used to store the cached items. A default table name has
| been provided but you're free to change it however you deem fit.
|
*/
'table' => 'cache',
/*
|--------------------------------------------------------------------------
| Memcached Servers
|--------------------------------------------------------------------------
|
| Now you may specify an array of your Memcached servers that should be
| used when utilizing the Memcached cache driver. All of the servers
| should contain a value for "host", "port", and "weight" options.
|
*/
'memcached' => array(
array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100),
),
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing a RAM based store such as APC or Memcached, there might
| be other applications utilizing the same cache. So, we'll specify a
| value to get prefixed to all our keys so we can avoid collisions.
|
*/
'prefix' => 'laravel',
);

172
app/config/cms.php Normal file
View File

@ -0,0 +1,172 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Specifies the default CMS theme.
|--------------------------------------------------------------------------
|
| This parameter value can be overridden by the CMS back-end settings.
|
*/
'activeTheme' => 'demo',
/*
|--------------------------------------------------------------------------
| Determines which modules to load
|--------------------------------------------------------------------------
|
| Specify which modules should be registered when using the application.
|
*/
'loadModules' => ['System', 'Backend', 'Cms'],
/*
|--------------------------------------------------------------------------
| Back-end URI prefix
|--------------------------------------------------------------------------
|
| Specifies the URI prefix used for accessing back-end pages.
|
*/
'backendUri' => 'backend',
/*
|--------------------------------------------------------------------------
| Back-end Skin
|--------------------------------------------------------------------------
|
| Specifies the back-end skin to use.
|
*/
'backendSkin' => 'Backend\Skins\Standard',
/*
|--------------------------------------------------------------------------
| Time to live for parsed CMS objects.
|--------------------------------------------------------------------------
|
| Specifies the number of minutes the CMS object cache lives. After the interval
| is expired item are re-cached. Note that items are re-cached automatically when
| the corresponding template file is modified.
|
*/
'parsedPageCacheTTL' => 0,
/*
|--------------------------------------------------------------------------
| Determines if the routing caching is enabled.
|--------------------------------------------------------------------------
|
| If the caching is enabled, the page URL map is saved in the cache. If a page
| URL was changed on the disk, the old URL value could be still saved in the cache.
| To update the cache the back-end Clear Cache feature should be used. It is recommended
| to disable the caching during the development, and enable it in the production mode.
|
*/
'enableRoutesCache' => false,
/*
|--------------------------------------------------------------------------
| Time to live for the URL map.
|--------------------------------------------------------------------------
|
| The URL map used in the CMS page routing process. By default
| the map is updated every time when a page is saved in the back-end or when the
| interval, in minutes, specified with the urlMapCacheTTL parameter expires.
|
*/
'urlCacheTtl' => 10,
/*
|--------------------------------------------------------------------------
| Determines if a friendly error page should be used.
|--------------------------------------------------------------------------
|
| If this value is set to true, a friendly error page is used when an
| exception is encountered. You must create a CMS page with route "/error"
| to set the contents of this page. Otherwise the default error page is shown.
|
*/
'customErrorPage' => false,
/*
|--------------------------------------------------------------------------
| Determines if the asset caching is enabled.
|--------------------------------------------------------------------------
|
| If the caching is enabled, combined assets are cached. If a asset file
| is changed on the disk, the old file contents could be still saved in the cache.
| To update the cache the back-end Clear Cache feature should be used. It is recommended
| to disable the caching during the development, and enable it in the production mode.
|
*/
'enableAssetCache' => false,
/*
|--------------------------------------------------------------------------
| Determines if the asset minifcation is enabled.
|--------------------------------------------------------------------------
|
| If the minifcation is enabled, combined assets are compressed (minified).
| It is recommended to disable the minification during the development, and
| enable it in the production mode.
|
*/
'enableAssetMinify' => false,
/*
|--------------------------------------------------------------------------
| Plugins directory
|--------------------------------------------------------------------------
|
| Specifies the plugins directory relative to the application root directory.
|
*/
'pluginsDir' => '/plugins',
/*
|--------------------------------------------------------------------------
| Themes directory
|--------------------------------------------------------------------------
|
| Specifies the themes directory relative to the application root directory.
|
*/
'themesDir' => '/themes',
/*
|--------------------------------------------------------------------------
| Uploads directory
|--------------------------------------------------------------------------
|
| Specifies the uploads directory relative to the application root directory.
|
*/
'uploadsDir' => '/uploads',
/*
|--------------------------------------------------------------------------
| Default permission mask
|--------------------------------------------------------------------------
|
| Specifies a default file and folder permission for newly created objects.
|
*/
'defaultMask' => ['file' => null, 'folder' => null],
);

124
app/config/database.php Normal file
View File

@ -0,0 +1,124 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| PDO Fetch Style
|--------------------------------------------------------------------------
|
| By default, database results will be returned as instances of the PHP
| stdClass object; however, you may desire to retrieve records in an
| array format for simplicity. Here you can tweak the fetch style.
|
*/
'fetch' => PDO::FETCH_CLASS,
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
'default' => 'mysql',
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => array(
'sqlite' => array(
'driver' => 'sqlite',
'database' => __DIR__.'/../database/production.sqlite',
'prefix' => '',
),
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
'pgsql' => array(
'driver' => 'pgsql',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
),
'sqlsrv' => array(
'driver' => 'sqlsrv',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => '',
'prefix' => '',
),
),
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk have not actually be run in the databases.
|
*/
'migrations' => 'migrations',
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer set of commands than a typical key-value systems
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => array(
'cluster' => true,
'default' => array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
),
),
);

2
app/config/dev/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

124
app/config/mail.php Normal file
View File

@ -0,0 +1,124 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Mail Driver
|--------------------------------------------------------------------------
|
| Laravel supports both SMTP and PHP's "mail" function as drivers for the
| sending of e-mail. You may specify which one you're using throughout
| your application here. By default, Laravel is setup for SMTP mail.
|
| Supported: "smtp", "mail", "sendmail"
|
*/
'driver' => 'smtp',
/*
|--------------------------------------------------------------------------
| SMTP Host Address
|--------------------------------------------------------------------------
|
| Here you may provide the host address of the SMTP server used by your
| applications. A default option is provided that is compatible with
| the Postmark mail service, which will provide reliable delivery.
|
*/
'host' => 'smtp.mailgun.org',
/*
|--------------------------------------------------------------------------
| SMTP Host Port
|--------------------------------------------------------------------------
|
| This is the SMTP port used by your application to delivery e-mails to
| users of your application. Like the host we have set this value to
| stay compatible with the Postmark e-mail application by default.
|
*/
'port' => 587,
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all e-mails sent by your application to be sent from
| the same address. Here, you may specify a name and address that is
| used globally for all e-mails that are sent by your application.
|
*/
'from' => array('address' => null, 'name' => null),
/*
|--------------------------------------------------------------------------
| E-Mail Encryption Protocol
|--------------------------------------------------------------------------
|
| Here you may specify the encryption protocol that should be used when
| the application send e-mail messages. A sensible default using the
| transport layer security protocol should provide great security.
|
*/
'encryption' => 'tls',
/*
|--------------------------------------------------------------------------
| SMTP Server Username
|--------------------------------------------------------------------------
|
| If your SMTP server requires a username for authentication, you should
| set it here. This will get used to authenticate with your server on
| connection. You may also set the "password" value below this one.
|
*/
'username' => null,
/*
|--------------------------------------------------------------------------
| SMTP Server Password
|--------------------------------------------------------------------------
|
| Here you may set the password required by your SMTP server to send out
| messages from your application. This will be given to the server on
| connection so that the application will be able to send messages.
|
*/
'password' => null,
/*
|--------------------------------------------------------------------------
| Sendmail System Path
|--------------------------------------------------------------------------
|
| When using the "sendmail" driver to send e-mails, we will need to know
| the path to where Sendmail lives on this server. A default path has
| been provided here, which will work well on most of your systems.
|
*/
'sendmail' => '/usr/sbin/sendmail -bs',
/*
|--------------------------------------------------------------------------
| Mail "Pretend"
|--------------------------------------------------------------------------
|
| When this option is enabled, e-mail will not actually be sent over the
| web and will instead be written to your application's logs files so
| you may inspect the message. This is great for local development.
|
*/
'pretend' => false,
);

82
app/config/queue.php Normal file
View File

@ -0,0 +1,82 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Default Queue Driver
|--------------------------------------------------------------------------
|
| The Laravel queue API supports a variety of back-ends via an unified
| API, giving you convenient access to each back-end using the same
| syntax for each one. Here you may set the default queue driver.
|
| Supported: "sync", "beanstalkd", "sqs", "iron"
|
*/
'default' => 'sync',
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection information for each server that
| is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more.
|
*/
'connections' => array(
'sync' => array(
'driver' => 'sync',
),
'beanstalkd' => array(
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
),
'sqs' => array(
'driver' => 'sqs',
'key' => 'your-public-key',
'secret' => 'your-secret-key',
'queue' => 'your-queue-url',
'region' => 'us-east-1',
),
'iron' => array(
'driver' => 'iron',
'project' => 'your-project-id',
'token' => 'your-token',
'queue' => 'your-queue-name',
),
'redis' => array(
'driver' => 'redis',
'queue' => 'default',
),
),
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control which database and table are used to store the jobs that
| have failed. You may change them to any database / table you wish.
|
*/
'failed' => array(
'database' => 'mysql', 'table' => 'failed_jobs',
),
);

127
app/config/session.php Normal file
View File

@ -0,0 +1,127 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "native", "cookie", "database", "apc",
| "memcached", "redis", "array"
|
*/
'driver' => 'native',
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle for it is expired. If you want them
| to immediately expire when the browser closes, set it to zero.
|
*/
'lifetime' => 120,
'expire_on_close' => false,
/*
|--------------------------------------------------------------------------
| Session File Location
|--------------------------------------------------------------------------
|
| When using the native session driver, we need a location where session
| files may be stored. A default has been set for you but a different
| location may be specified. This is only needed for file sessions.
|
*/
'files' => storage_path().'/sessions',
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the database
| connection that should be used to manage your sessions. This should
| correspond to a connection in your "database" configuration file.
|
*/
'connection' => null,
/*
|--------------------------------------------------------------------------
| Session Database Table
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the table we
| should use to manage the sessions. Of course, a sensible default is
| provided for you; however, you are free to change this as needed.
|
*/
'table' => 'sessions',
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery
|--------------------------------------------------------------------------
|
| Some session drivers must manually sweep their storage location to get
| rid of old sessions from storage. Here are the chances that it will
| happen on a given request. By default, the odds are 2 out of 100.
|
*/
'lottery' => array(2, 100),
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the cookie used to identify a session
| instance by ID. The name specified here will get used every time a
| new session cookie is created by the framework for every driver.
|
*/
'cookie' => 'october_session',
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application but you are free to change this when necessary.
|
*/
'path' => '/',
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| Here you may change the domain of the cookie used to identify a session
| in your application. This will determine which domains the cookie is
| available to in your application. A sensible default has been set.
|
*/
'domain' => null,
);

View File

@ -0,0 +1,99 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Plugins directory
|--------------------------------------------------------------------------
|
| Specifies the plugins directory relative to the application root directory.
|
*/
'pluginsDir' => '/tests/fixtures/system/plugins',
/*
|--------------------------------------------------------------------------
| Themes directory
|--------------------------------------------------------------------------
|
| Specifies the themes directory relative to the application root directory.
|
*/
'themesDir' => '/tests/fixtures/cms/themes',
/*
|--------------------------------------------------------------------------
| Time to live for parsed CMS objects.
|--------------------------------------------------------------------------
|
| Specifies the number of minutes the CMS object cache lives. After the interval
| is expired item are re-cached. Note that items are re-cached automatically when
| the corresponding template file is modified.
|
*/
'parsedPageCacheTTL' => 1440,
/*
|--------------------------------------------------------------------------
| Determines if the routing caching is enabled.
|--------------------------------------------------------------------------
|
| If the caching is enabled, the page URL map is saved in the cache. If a page
| URL was changed on the disk, the old URL value could be still saved in the cache.
| To update the cache the back-end Clear Cache feature should be used. It is recommended
| to disable the caching during the development, and enable it in the production mode.
|
*/
'enableRoutesCache' => true,
/*
|--------------------------------------------------------------------------
| Time to live for the URL map.
|--------------------------------------------------------------------------
|
| The URL map used in the CMS page routing process. By default
| the map is updated every time when a page is saved in the back-end or when the
| interval, in minutes, specified with the urlMapCacheTTL parameter expires.
|
*/
'urlCacheTtl' => 1,
/*
|--------------------------------------------------------------------------
| Specifies the default CMS theme
|--------------------------------------------------------------------------
|
| This parameter value can be overridden by the CMS back-end settings.
|
*/
'activeTheme' => 'test',
/*
|--------------------------------------------------------------------------
| Determines if the asset caching is enabled.
|--------------------------------------------------------------------------
|
| If the caching is enabled, combined assets are cached. If a asset file
| is changed on the disk, the old file contents could be still saved in the cache.
| To update the cache the back-end Clear Cache feature should be used. It is recommended
| to disable the caching during the development, and enable it in the production mode.
|
*/
'enableAssetCache' => false,
/*
|--------------------------------------------------------------------------
| Disables Twig caching for unit tests
|--------------------------------------------------------------------------
*/
'twigNoCache' => true,
);

31
app/config/view.php Normal file
View File

@ -0,0 +1,31 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| View Storage Paths
|--------------------------------------------------------------------------
|
| Most templating systems load templates from disk. Here you may specify
| an array of paths that should be checked for your views. Of course
| the usual Laravel view path has already been registered for you.
|
*/
'paths' => array(__DIR__.'/../views'),
/*
|--------------------------------------------------------------------------
| Pagination View
|--------------------------------------------------------------------------
|
| This view will be used to render the pagination link output, and can
| be easily customized here to show any view you like. A clean view
| compatible with Twitter's Bootstrap is given to you by default.
|
*/
'pagination' => 'pagination::slider',
);

80
app/filters.php Normal file
View File

@ -0,0 +1,80 @@
<?php
/*
|--------------------------------------------------------------------------
| Application & Route Filters
|--------------------------------------------------------------------------
|
| Below you will find the "before" and "after" events for the application
| which may be used to do any work before or after a request into your
| application. Here you may also register your custom route filters.
|
*/
App::before(function($request)
{
//
});
App::after(function($request, $response)
{
//
});
/*
|--------------------------------------------------------------------------
| Authentication Filters
|--------------------------------------------------------------------------
|
| The following filters are used to verify that the user of the current
| session is logged into this application. The "basic" filter easily
| integrates HTTP Basic authentication for quick, simple checking.
|
*/
Route::filter('auth', function()
{
// if (Auth::guest()) return Redirect::guest('login');
});
Route::filter('auth.basic', function()
{
// return Auth::basic();
});
/*
|--------------------------------------------------------------------------
| Guest Filter
|--------------------------------------------------------------------------
|
| The "guest" filter is the counterpart of the authentication filters as
| it simply checks that the current user is not logged in. A redirect
| response will be issued if they are, which you may freely change.
|
*/
Route::filter('guest', function()
{
// if (Auth::check()) return Redirect::to('/');
});
/*
|--------------------------------------------------------------------------
| CSRF Protection Filter
|--------------------------------------------------------------------------
|
| The CSRF filter is responsible for protecting your application against
| cross-site request forgery attacks. If this special token in a user
| session does not match the one given in this request, we'll bail.
|
*/
Route::filter('csrf', function()
{
if (Session::token() != Input::get('_token'))
{
throw new Illuminate\Session\TokenMismatchException;
}
});

12
app/routes.php Normal file
View File

@ -0,0 +1,12 @@
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the Closure to execute when that URI is requested.
|
*/

13
app/start/artisan.php Normal file
View File

@ -0,0 +1,13 @@
<?php
/*
|--------------------------------------------------------------------------
| Register The Artisan Commands
|--------------------------------------------------------------------------
|
| Each available Artisan command must be registered with the console so
| that it is available to be called. We'll register every command so
| the console gets access to each of the command object instances.
|
*/

81
app/start/global.php Normal file
View File

@ -0,0 +1,81 @@
<?php
/*
|--------------------------------------------------------------------------
| Register The Laravel Class Loader
|--------------------------------------------------------------------------
|
| In addition to using Composer, you may use the Laravel class loader to
| load your controllers and models. This is useful for keeping all of
| your classes in the "global" namespace without Composer updating.
|
*/
ClassLoader::addDirectories(array(
app_path().'/commands',
app_path().'/controllers',
app_path().'/models',
app_path().'/database/seeds',
));
/*
|--------------------------------------------------------------------------
| Application Error Logger
|--------------------------------------------------------------------------
|
| Here we will configure the error logger setup for the application which
| is built on top of the wonderful Monolog library. By default we will
| build a basic log file setup which creates a single file for logs.
|
*/
Log::useFiles(storage_path().'/logs/system.log');
/*
|--------------------------------------------------------------------------
| Application Error Handler
|--------------------------------------------------------------------------
|
| Here you may handle any errors that occur in your application, including
| logging them or displaying custom views for specific errors. You may
| even register several error handlers to handle different types of
| exceptions. If nothing is returned, the default error view is
| shown, which includes a detailed stack trace during debug.
|
*/
App::error(function(Exception $exception, $code)
{
Log::error($exception);
});
/*
|--------------------------------------------------------------------------
| Maintenance Mode Handler
|--------------------------------------------------------------------------
|
| The "down" Artisan command gives you the ability to put an application
| into maintenance mode. Here, you will define what is displayed back
| to the user if maintenace mode is in effect for this application.
|
*/
App::down(function()
{
return Response::make("Be right back!", 503);
});
/*
|--------------------------------------------------------------------------
| Require The Filters File
|--------------------------------------------------------------------------
|
| Next we will load the filters file for the application. This gives us
| a nice separate location to store our route and application filter
| definitions instead of putting them all in the main routes file.
|
*/
require app_path().'/filters.php';

1
app/storage/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
services.manifest

2
app/storage/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
app/storage/combiner/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
app/storage/logs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
app/storage/meta/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
app/storage/sessions/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
app/storage/twig/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
app/storage/views/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

73
bootstrap/autoload.php Normal file
View File

@ -0,0 +1,73 @@
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Composer Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/
require __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Include The Compiled Class File
|--------------------------------------------------------------------------
|
| To dramatically increase your application's performance, you may use a
| compiled class file which contains all of the classes commonly used
| by a request. The Artisan "optimize" is used to create this file.
|
*/
if (file_exists($compiled = __DIR__.'/compiled.php'))
{
require $compiled;
}
/*
|--------------------------------------------------------------------------
| Setup Patchwork UTF-8 Handling
|--------------------------------------------------------------------------
|
| The Patchwork library provides solid handling of UTF-8 strings as well
| as provides replacements for all mb_* and iconv type functions that
| are not available by default in PHP. We'll setup this stuff here.
|
*/
Patchwork\Utf8\Bootup::initMbstring();
/*
|--------------------------------------------------------------------------
| Register The October Auto Loader
|--------------------------------------------------------------------------
| This should come before the Laravel loader because it is more likely
| to find a partner fo' life.
|
*/
October\Rain\Support\ClassLoader::register();
October\Rain\Support\ClassLoader::addDirectories(array(__DIR__.'/../modules', __DIR__.'/../plugins'));
/*
|--------------------------------------------------------------------------
| Register The Laravel Auto Loader
|--------------------------------------------------------------------------
|
| We register an auto-loader "behind" the Composer loader that can load
| model classes on the fly, even if the autoload files have not been
| regenerated for the application. We'll add it to the stack here.
|
*/
Illuminate\Support\ClassLoader::register();

57
bootstrap/paths.php Normal file
View File

@ -0,0 +1,57 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Application Path
|--------------------------------------------------------------------------
|
| Here we just defined the path to the application directory. Most likely
| you will never need to change this value as the default setup should
| work perfectly fine for the vast majority of all our applications.
|
*/
'app' => __DIR__.'/../app',
/*
|--------------------------------------------------------------------------
| Public Path
|--------------------------------------------------------------------------
|
| The public path contains the assets for your web application, such as
| your JavaScript and CSS files, and also contains the primary entry
| point for web requests into these applications from the outside.
|
*/
'public' => __DIR__.'/..',
/*
|--------------------------------------------------------------------------
| Base Path
|--------------------------------------------------------------------------
|
| The base path is the root of the Laravel installation. Most likely you
| will not need to change this value. But, if for some wild reason it
| is necessary you will do so here, just proceed with some caution.
|
*/
'base' => __DIR__.'/..',
/*
|--------------------------------------------------------------------------
| Storage Path
|--------------------------------------------------------------------------
|
| The storage path is used by Laravel to store cached Blade views, logs
| and other pieces of information. You may modify the path here when
| you want to change the location of this directory for your apps.
|
*/
'storage' => __DIR__.'/../app/storage',
);

86
bootstrap/start.php Normal file
View File

@ -0,0 +1,86 @@
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application;
/*
|--------------------------------------------------------------------------
| Detect The Application Environment
|--------------------------------------------------------------------------
|
| Laravel takes a dead simple approach to your application environments
| so you can just specify a machine name or HTTP host that matches a
| given environment, then we will automatically detect it for you.
|
*/
$env = $app->detectEnvironment(function(){
return isset($_SERVER['CMS_ENV']) ? $_SERVER['CMS_ENV'] : null;
});
/*
|--------------------------------------------------------------------------
| Bind Paths
|--------------------------------------------------------------------------
|
| Here we are binding the paths configured in paths.php to the app. You
| should not be changing these here. If you need to change these you
| may do so within the paths.php file and they will be bound here.
|
*/
$app->bindInstallPaths(require __DIR__.'/paths.php');
/*
|--------------------------------------------------------------------------
| Load The Application
|--------------------------------------------------------------------------
|
| Here we will load the Illuminate application. We'll keep this is in a
| separate location so we can isolate the creation of an application
| from the actual running of the application with a given request.
|
*/
$framework = $app['path.base'].'/vendor/laravel/framework/src';
require $framework.'/Illuminate/Foundation/start.php';
/*
|--------------------------------------------------------------------------
| Disable any caching
|--------------------------------------------------------------------------
*/
if (!isset($unitTesting) || !$unitTesting) {
header('Cache-Control: no-store, private, no-cache, must-revalidate'); // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0, max-stale = 0', false); // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Expires: 0', false);
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Pragma: no-cache');
}
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;

View File

@ -1,5 +1,11 @@
<?php namespace Backend;
use App;
use Lang;
use Backend;
use BackendMenu;
use BackendAuth;
use Backend\Classes\WidgetManager;
use October\Rain\Support\ModuleServiceProvider;
class ServiceProvider extends ModuleServiceProvider
@ -12,6 +18,58 @@ class ServiceProvider extends ModuleServiceProvider
public function register()
{
parent::register('backend');
/*
* Register widgets
*/
WidgetManager::instance()->registerFormWidgets(function($manager){
$manager->registerFormWidget('Backend\FormWidgets\CodeEditor', [
'label' => 'Code editor',
'alias' => 'codeeditor'
]);
$manager->registerFormWidget('Backend\FormWidgets\RichEditor', [
'label' => 'Rich editor',
'alias' => 'richeditor'
]);
$manager->registerFormWidget('Backend\FormWidgets\FileUpload', [
'label' => 'File uploader',
'alias' => 'fileupload'
]);
$manager->registerFormWidget('Backend\FormWidgets\Relation', [
'label' => 'Relationship',
'alias' => 'relation'
]);
$manager->registerFormWidget('Backend\FormWidgets\Datepicker', [
'label' => 'Date picker',
'alias' => 'datepicker'
]);
});
/*
* Register navigation
*/
BackendMenu::registerCallback(function($manager) {
$manager->registerMenuItems('October.Backend', [
'dashboard' => [
'label' => 'backend::lang.dashboard.menu_label',
'icon' => 'icon-home',
'url' => Backend::url('backend'),
'permissions' => ['backend.access_dashboard'],
'order' => 1
]
]);
});
/*
* Register permissions
*/
BackendAuth::registerCallback(function($manager) {
$manager->registerPermissions('October.Backend', [
'backend.access_dashboard' => ['label' => 'View the dashboard', 'tab' => 'System'],
'backend.manage_users' => ['label' => 'Manage other administrators', 'tab' => 'System'],
]);
});
}
/**
@ -23,4 +81,5 @@ class ServiceProvider extends ModuleServiceProvider
{
parent::boot('backend');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
&amp;quot;License&amp;quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
&amp;quot;Licensor&amp;quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
&amp;quot;Legal Entity&amp;quot; shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, &amp;quot;control&amp;quot; means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
&amp;quot;You&amp;quot; (or &amp;quot;Your&amp;quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
&amp;quot;Source&amp;quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
&amp;quot;Object&amp;quot; form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
&amp;quot;Work&amp;quot; shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
&amp;quot;Derivative Works&amp;quot; shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
&amp;quot;Contribution&amp;quot; shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, &amp;quot;submitted&amp;quot; means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as &amp;quot;Not a Contribution.&amp;quot;
&amp;quot;Contributor&amp;quot; shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a &amp;quot;NOTICE&amp;quot; text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an &amp;quot;AS IS&amp;quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="41px" height="41px" viewBox="134 73 41 41" enable-background="new 134 73 41 41" xml:space="preserve">
<defs>
</defs>
<rect x="0.5" y="0.5" display="none" fill="#B8CC44" stroke="#FFFFFF" width="315" height="291"/>
<path id="bg_1_" opacity="0" fill="#FFFFFF" d="M135.015,93.554c0.002-10.854,8.8-19.653,19.654-19.655
c10.856,0.002,19.653,8.802,19.655,19.657c-0.001,6.679-3.331,12.577-8.422,16.129c-3.184,2.223-7.056,3.525-11.232,3.526
C143.815,113.209,135.016,104.41,135.015,93.554z"/>
<path opacity="0" fill="none" stroke="#2A98DB" stroke-width="6" d="M137.644,93.554c0.001-9.402,7.623-17.024,17.025-17.026
c9.404,0.002,17.025,7.625,17.026,17.028c0,5.785-2.886,10.896-7.295,13.972c-2.758,1.925-6.112,3.054-9.73,3.055
C145.267,110.58,137.645,102.959,137.644,93.554z"/>
<path fill="none" stroke="#5FB6F5" stroke-width="5.28" stroke-linecap="round" stroke-linejoin="round" d="M142.114,105.059
c-4.002-4.376-5.601-10.721-3.64-16.765c1.318-4.052,4.011-7.271,7.39-9.312"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="41px" height="41px" viewBox="134 73 41 41" enable-background="new 134 73 41 41" xml:space="preserve">
<defs>
</defs>
<rect x="0.5" y="0.5" display="none" fill="#B8CC44" stroke="#FFFFFF" width="315" height="291"/>
<path id="bg_1_" display="none" fill="#FFFFFF" d="M135.015,93.554c0.002-10.854,8.8-19.653,19.654-19.655
c10.856,0.002,19.653,8.802,19.655,19.657c-0.001,6.679-3.331,12.577-8.422,16.129c-3.184,2.223-7.056,3.525-11.232,3.526
C143.815,113.209,135.016,104.41,135.015,93.554z"/>
<path opacity="0" fill="none" stroke="#2A98DB" stroke-width="6" d="M137.644,93.554c0.001-9.402,7.623-17.024,17.025-17.026
c9.404,0.002,17.025,7.625,17.026,17.028c0,5.785-2.886,10.896-7.295,13.972c-2.758,1.925-6.112,3.054-9.73,3.055
C145.267,110.58,137.645,102.959,137.644,93.554z"/>
<path fill="none" stroke="#FFFFFF" stroke-width="5.28" stroke-linecap="round" stroke-linejoin="round" d="M142.114,105.059
c-4.002-4.376-5.601-10.721-3.64-16.765c1.318-4.052,4.011-7.271,7.39-9.312"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="41px" height="41px" viewBox="134 73 41 41" enable-background="new 134 73 41 41" xml:space="preserve">
<defs>
</defs>
<rect x="0.5" y="0.5" display="none" fill="#B8CC44" stroke="#FFFFFF" width="315" height="291"/>
<path id="bg_1_" fill="#FFFFFF" d="M135.015,93.554c0.002-10.854,8.8-19.653,19.654-19.655c10.856,0.002,19.653,8.802,19.655,19.657
c-0.001,6.679-3.331,12.577-8.422,16.129c-3.184,2.223-7.056,3.525-11.232,3.526C143.815,113.209,135.016,104.41,135.015,93.554z"/>
<path opacity="0" fill="none" stroke="#2A98DB" stroke-width="6" d="M137.644,93.554c0.001-9.402,7.623-17.024,17.025-17.026
c9.404,0.002,17.025,7.625,17.026,17.028c0,5.785-2.886,10.896-7.295,13.972c-2.758,1.925-6.112,3.054-9.73,3.055
C145.267,110.58,137.645,102.959,137.644,93.554z"/>
<path fill="none" stroke="#5FB6F5" stroke-width="5.28" stroke-linecap="round" stroke-linejoin="round" d="M142.114,105.059
c-4.002-4.376-5.601-10.721-3.64-16.765c1.318-4.052,4.011-7.271,7.39-9.312"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.0"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="408px" height="71px" viewBox="-5.208 -4.384 408 71" enable-background="new -5.208 -4.384 408 71"
xml:space="preserve">
<defs>
</defs>
<path opacity="0.3" fill="#596667" d="M111.401,42.425c-1.112,0-2.896,0.109-4.007,1.986c-0.441,0.744-0.816,1.564-1.631,2.516
c-1.481,1.727-3.297,3.045-5.44,3.952c-2.146,0.908-4.589,1.36-7.325,1.36c-2.766,0-5.23-0.452-7.385-1.36
c-2.162-0.907-3.98-2.226-5.462-3.952c-1.485-1.73-2.618-3.84-3.411-6.334c-0.789-2.489-1.185-5.306-1.185-8.444
c0-3.114,0.396-5.913,1.185-8.406c0.793-2.489,1.926-4.603,3.411-6.33c1.482-1.729,3.3-3.052,5.462-3.976
c2.155-0.922,4.619-1.383,7.385-1.383c2.736,0,5.179,0.461,7.325,1.383c2.143,0.924,3.959,2.247,5.44,3.976
c0.716,0.834,1.352,1.757,1.903,2.772c1.064,1.96,3.735,1.726,3.735,1.726h12.112c-0.248-0.817-0.505-1.63-0.817-2.422
c-1.538-3.918-3.702-7.318-6.497-10.198c-2.794-2.88-6.16-5.148-10.097-6.807C102.163,0.827,97.799,0,92.998,0
C88.2,0,83.827,0.82,79.876,2.462c-3.951,1.642-7.331,3.902-10.14,6.783c-2.811,2.884-4.981,6.28-6.521,10.197
c-0.092,0.235-0.173,0.476-0.26,0.713c-0.083-0.222-0.159-0.447-0.247-0.667c-1.562-3.918-3.757-7.318-6.594-10.198
c-2.835-2.879-6.251-5.147-10.248-6.807c-3.997-1.656-8.428-2.483-13.299-2.483c-4.87,0-9.308,0.819-13.319,2.461
c-4.01,1.642-7.439,3.903-10.29,6.784c-2.854,2.883-5.056,6.279-6.619,10.196C0.78,23.363,0,27.598,0,32.148
c0,4.553,0.78,8.787,2.34,12.705c1.563,3.917,3.765,7.324,6.619,10.218c2.851,2.894,6.28,5.162,10.29,6.804
c4.011,1.643,8.449,2.465,13.319,2.465c4.871,0,9.302-0.822,13.299-2.465c3.997-1.642,7.413-3.91,10.248-6.804
c2.837-2.894,5.032-6.301,6.594-10.218c0.091-0.228,0.169-0.46,0.255-0.689c0.084,0.229,0.162,0.462,0.251,0.689
c1.54,3.917,3.71,7.324,6.521,10.218c2.809,2.894,6.189,5.162,10.14,6.804c3.951,1.643,8.324,2.465,13.122,2.465
c4.801,0,9.165-0.822,13.104-2.465c3.937-1.642,7.303-3.91,10.097-6.804c2.795-2.894,4.959-6.301,6.497-10.218
c0.312-0.793,0.57-1.609,0.818-2.428H111.401z M48.981,40.593c-0.8,2.494-1.954,4.604-3.458,6.334
c-1.503,1.727-3.346,3.045-5.521,3.951c-2.178,0.909-4.657,1.361-7.434,1.361c-2.807,0-5.308-0.452-7.496-1.361
c-2.193-0.906-4.038-2.225-5.543-3.951c-1.506-1.73-2.657-3.84-3.462-6.334c-0.8-2.489-1.202-5.306-1.202-8.445
c0-3.113,0.402-5.912,1.202-8.405c0.805-2.489,1.956-4.603,3.462-6.329c1.505-1.73,3.35-3.053,5.543-3.977
c2.188-0.921,4.689-1.382,7.496-1.382c2.777,0,5.256,0.461,7.434,1.382c2.175,0.924,4.018,2.247,5.521,3.977
c1.504,1.726,2.658,3.84,3.458,6.329c0.802,2.493,1.204,5.292,1.204,8.405C50.185,35.288,49.783,38.104,48.981,40.593z"/>
<path opacity="0.3" fill="#596667" d="M208.964,32.148c0,4.553-0.784,8.787-2.341,12.705c-1.564,3.917-3.761,7.324-6.594,10.218
c-2.838,2.894-6.252,5.162-10.251,6.804c-3.996,1.643-8.427,2.465-13.299,2.465c-4.87,0-9.307-0.822-13.317-2.465
c-4.012-1.642-7.442-3.91-10.29-6.804c-2.856-2.894-5.059-6.301-6.62-10.218c-1.56-3.918-2.34-8.152-2.34-12.705
c0-4.55,0.78-8.785,2.34-12.706c1.561-3.917,3.764-7.313,6.62-10.196c2.848-2.881,6.278-5.142,10.29-6.784
c4.01-1.642,8.447-2.461,13.317-2.461c4.872,0,9.303,0.827,13.299,2.483c3.999,1.66,7.413,3.928,10.251,6.807
c2.833,2.88,5.029,6.28,6.594,10.198C208.18,23.406,208.964,27.626,208.964,32.148 M194.097,32.148c0-3.113-0.401-5.912-1.202-8.405
c-0.8-2.489-1.954-4.603-3.458-6.329c-1.504-1.73-3.347-3.053-5.521-3.977c-2.179-0.921-4.658-1.382-7.436-1.382
c-2.806,0-5.306,0.461-7.494,1.382c-2.195,0.924-4.04,2.247-5.544,3.977c-1.506,1.726-2.658,3.84-3.462,6.329
c-0.8,2.493-1.203,5.292-1.203,8.405c0,3.14,0.403,5.956,1.203,8.445c0.804,2.494,1.956,4.604,3.462,6.334
c1.504,1.727,3.349,3.045,5.544,3.951c2.188,0.909,4.688,1.361,7.494,1.361c2.778,0,5.257-0.452,7.436-1.361
c2.174-0.906,4.017-2.225,5.521-3.951c1.504-1.73,2.658-3.84,3.458-6.334C193.696,38.104,194.097,35.288,194.097,32.148"/>
<path opacity="0.3" fill="#596667" d="M210.603,63.648V0.69h23.159c4.351,0,8.053,0.405,11.104,1.21
c3.053,0.808,5.546,1.944,7.475,3.415c1.931,1.468,3.334,3.255,4.214,5.358c0.878,2.103,1.319,4.452,1.319,7.044
c0,1.41-0.204,2.77-0.606,4.082c-0.404,1.312-1.036,2.542-1.902,3.696c-0.864,1.15-1.971,2.193-3.326,3.133
c-1.354,0.935-2.982,1.748-4.885,2.44c4.151,1.01,7.217,2.645,9.208,4.906c1.985,2.262,2.979,5.161,2.979,8.705
c0,2.676-0.519,5.17-1.556,7.473c-1.037,2.307-2.558,4.314-4.557,6.031c-2.004,1.713-4.474,3.053-7.412,4.016
c-2.938,0.967-6.294,1.449-10.069,1.449H210.603z M225.208,27.178h7.604c1.612,0,3.066-0.114,4.365-0.343
c1.294-0.232,2.397-0.633,3.306-1.21c0.907-0.577,1.596-1.369,2.074-2.378c0.476-1.009,0.71-2.275,0.71-3.804
c0-1.494-0.185-2.742-0.56-3.738c-0.375-0.991-0.952-1.79-1.73-2.398c-0.775-0.605-1.765-1.034-2.958-1.294
c-1.197-0.26-2.614-0.39-4.256-0.39h-8.555V27.178z M225.208,36.944V52.63h10.279c1.931,0,3.511-0.246,4.735-0.734
c1.224-0.492,2.183-1.126,2.872-1.902c0.692-0.777,1.166-1.656,1.426-2.639c0.259-0.977,0.389-1.971,0.389-2.978
c0-1.153-0.15-2.19-0.452-3.112c-0.302-0.921-0.82-1.698-1.554-2.334c-0.736-0.634-1.711-1.123-2.921-1.471
c-1.21-0.344-2.735-0.517-4.578-0.517H225.208z"/>
<polygon opacity="0.3" fill="#596667" points="301.985,0.689 301.985,11.924 276.144,11.924 276.144,26.616 295.934,26.616
295.934,37.419 276.144,37.419 276.144,52.412 301.985,52.412 301.985,63.647 261.454,63.647 261.454,0.689 "/>
<path opacity="0.3" fill="#596667" d="M319.388,40.358v23.29h-14.604V0.689h20.481c4.549,0,8.431,0.472,11.646,1.407
c3.211,0.936,5.835,2.24,7.862,3.91c2.032,1.672,3.506,3.643,4.433,5.919c0.919,2.275,1.38,4.753,1.38,7.434
c0,2.046-0.274,3.973-0.823,5.787c-0.543,1.815-1.36,3.496-2.438,5.036c-1.08,1.54-2.406,2.915-3.975,4.126
c-1.572,1.211-3.38,2.203-5.426,2.98c0.98,0.492,1.896,1.098,2.745,1.814c0.848,0.721,1.592,1.6,2.229,2.637l13.393,21.908h-13.221
c-2.451,0-4.207-0.922-5.272-2.764l-10.457-18.279c-0.461-0.809-1.003-1.382-1.622-1.73c-0.617-0.344-1.491-0.517-2.611-0.517
H319.388z M319.388,30.291h5.877c1.985,0,3.679-0.253,5.076-0.756c1.397-0.506,2.544-1.203,3.437-2.097
c0.894-0.892,1.54-1.937,1.944-3.131c0.4-1.197,0.606-2.487,0.606-3.869c0-2.764-0.902-4.927-2.702-6.48
c-1.8-1.558-4.588-2.335-8.361-2.335h-5.877V30.291z"/>
<path opacity="0.3" fill="#596667" d="M127.329,0.689v25.243h-16.043c-1.452,0-2.628,1.128-2.628,2.517v7.438
c0,1.392,1.176,2.519,2.628,2.519h16.043v25.241h14.604V0.689H127.329z"/>
<path opacity="0.3" fill="#DB6B26" d="M400.482,30.821c-1.021-16.438-19.171-30.662-26.648-30.819
c-7.479-0.159-21.459,17.25-22.024,34.938c-0.53,16.585,13.818,27.958,19.996,29.401c0.172-7.803,0.805-36.532,0.911-39.882
c0.223-6.846,0.563-17.498,1.723-17.852c0.665-0.203,1.571,5.785,1.46,9.324c-0.023,0.675-0.031,2.862-0.031,5.966
c1.251-1.255,2.558-2.558,3.352-3.328c2.177-2.11,3.14-1.859,2.096,0.023c-0.643,1.158-3.408,5.008-5.441,7.798
c0.012,5.627,0.037,12.709,0.064,19.309c2.871-3.315,6.386-7.289,8.232-9.057c3.872-3.702,4.284-3.235,2.455,0.651
c-1.123,2.382-6.614,8.736-10.668,13.747c0.025,5.855,0.045,10.778,0.05,13.129C386.88,61.81,401.481,46.96,400.482,30.821"/>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

View File

@ -0,0 +1,5 @@
$(document).ready(function(){
$(document.body).removeClass('preload')
$('form input[type=text], form input[type=password]').first().focus()
})

View File

@ -0,0 +1,63 @@
/*
* Balloon selector control.
*
* Data attributes:
* - data-control="balloon-selector" - enables the plugin
*
*/
+function ($) { "use strict";
var BalloonSelector = function (element, options) {
this.$el = $(element)
this.$field = $('input', this.$el)
this.options = options || {};
var self = this;
$('li', this.$el).click(function(){
if (self.$el.hasClass('control-disabled'))
return
$('li', self.$el).removeClass('active')
$(this).addClass('active')
self.$field.val($(this).data('value'))
self.$el.trigger('change')
})
}
BalloonSelector.DEFAULTS = {}
// BALLOON SELECTOR PLUGIN DEFINITION
// ===================================
var old = $.fn.balloonSelector
$.fn.balloonSelector = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.balloon-selector')
var options = $.extend({}, BalloonSelector.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.balloon-selector', (data = new BalloonSelector(this, options)))
})
}
$.fn.balloonSelector.Constructor = BalloonSelector
// BALLOON SELECTOR NO CONFLICT
// ===================================
$.fn.balloonSelector.noConflict = function () {
$.fn.balloonSelector = old
return this
}
// BALLOON SELECTOR DATA-API
// ===================================
$(document).on('render', function(){
$('div[data-control=balloon-selector]').balloonSelector()
})
}(window.jQuery);

View File

@ -0,0 +1,140 @@
/*
* The form change monitor API.
*
* This plugin allows to monitor changes in a form.
* The script adds the "oc-data-changed" class to the form element when the form data is changed.
*
* Supported data attributes:
* - data-change-monitor - enables the plugin form a form
* - data-window-close-confirm - confirmation message to show when a browser window is closing and there is unsaved data
*
* Example: <form
* data-change-monitor
data-window-close-confirm="There is unsaved data"
* ...
* >
*
* Supported events:
* - change - marks the form data as "changed". The event can be triggered on any
* element within a form or on a form itself.
* - unchange.oc.changeMonitor - marks the form data as "unchanged". The event can be triggered on any
* element within a form or on a form itself.
* - pause.oc.changeMonitor - temporary pauses the change monitoring. The event can be triggered on any
* element within a form or on a form itself.
* - resume.oc.changeMonitor - resumes the change monitoring. The event can be triggered on any
* element within a form or on a form itself.
*
* Triggered events:
* - changed.oc.changeMonitor - triggered when the form data changes.
* - unchanged.oc.changeMonitor - triggered when the form data unchanges.
*
* JavaScript API:
* $('#form').changeMonitor()
*/
+function ($) { "use strict";
var ChangeMonitor = function (element, options) {
var $el = this.$el = $(element);
this.paused = false
this.options = options || {}
this.init()
}
ChangeMonitor.prototype.init = function() {
this.$el.on('change', $.proxy(this.change, this))
this.$el.on('unchange.oc.changeMonitor', $.proxy(this.unchange, this))
this.$el.on('pause.oc.changeMonitor ', $.proxy(this.pause, this))
this.$el.on('resume.oc.changeMonitor ', $.proxy(this.resume, this))
this.$el.on('keyup input paste', 'input, textarea:not(.ace_text-input)', $.proxy(this.onInputChange, this))
$('input:not([type=hidden]), textarea:not(.ace_text-input)', this.$el).each(function() {
$(this).data('oldval.oc.changeMonitor', $(this).val());
})
if (this.options.windowCloseConfirm)
$(window).on('beforeunload', $.proxy(this.onBeforeUnload, this))
}
ChangeMonitor.prototype.change = function(ev) {
if (this.paused)
return
if (!this.$el.hasClass('oc-data-changed')) {
this.$el.trigger('changed.oc.changeMonitor')
this.$el.addClass('oc-data-changed')
}
}
ChangeMonitor.prototype.unchange = function() {
if (this.paused)
return
if (this.$el.hasClass('oc-data-changed')) {
this.$el.trigger('unchanged.oc.changeMonitor')
this.$el.removeClass('oc-data-changed')
}
}
ChangeMonitor.prototype.onInputChange = function(ev) {
if (this.paused)
return
var $el = $(ev.target)
if ($el.data('oldval.oc.changeMonitor') != $el.val()) {
$el.data('oldval.oc.changeMonitor', $el.val());
this.change(ev);
}
}
ChangeMonitor.prototype.pause = function() {
this.paused = true
}
ChangeMonitor.prototype.resume = function() {
this.paused = false
}
ChangeMonitor.prototype.onBeforeUnload = function() {
if ($.contains(document.documentElement, this.$el.get(0)) && this.$el.hasClass('oc-data-changed'))
return this.options.windowCloseConfirm
}
ChangeMonitor.DEFAULTS = {
windowCloseConfirm: false
}
// CHANGEMONITOR PLUGIN DEFINITION
// ===============================
var old = $.fn.changeMonitor
$.fn.changeMonitor = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.changeMonitor')
var options = $.extend({}, ChangeMonitor.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.changeMonitor', (data = new ChangeMonitor(this, options)))
})
}
$.fn.changeMonitor.Constructor = ChangeMonitor
// CHANGEMONITOR NO CONFLICT
// ===============================
$.fn.changeMonitor.noConflict = function () {
$.fn.changeMonitor = old
return this
}
// CHANGEMONITOR DATA-API
// ===============================
$(document).render(function(){
$('[data-change-monitor]').changeMonitor()
})
}(window.jQuery);

View File

@ -0,0 +1,140 @@
/*
* The bar chart plugin.
*
* Data attributes:
* - data-control="chart-bar" - enables the bar chart plugin
* - data-height="200" - optional, height of the graph
* - data-full-width="1" - optional, determines whether the chart should use the full width of the container
*
* JavaScript API:
* $('.scoreboard .chart').barChart()
*
* Dependences:
* - Raphaël (raphael-min.js)
* - October chart utilities (october.chartutils.js)
*/
+function ($) { "use strict";
var BarChart = function (element, options) {
this.options = options || {};
var
$el = this.$el = $(element),
size = this.size = $el.height(),
total = 0,
self = this,
values = $.oc.chartUtils.loadListValues($('ul', $el)),
$legend = $.oc.chartUtils.createLegend($('ul', $el)),
indicators = $.oc.chartUtils.initLegendColorIndicators($legend),
isFullWidth = this.isFullWidth(),
chartHeight = this.options.height !== undefined ? this.options.height : size,
chartWidth = isFullWidth ? this.$el.width() : size,
barWidth = (chartWidth - (values.values.length-1)*this.options.gap)/values.values.length
var $canvas = $('<div/>').addClass('canvas').height(chartHeight).width(isFullWidth ? '100%' : chartWidth)
$el.prepend($canvas)
$el.toggleClass('full-width', isFullWidth)
Raphael($canvas.get(0), isFullWidth ? '100%' : chartWidth, chartHeight, function(){
self.paper = this;
self.bars = this.set()
self.paper.customAttributes.bar = function (start, height) {
return {
path: [
["M", start, chartWidth],
["L", start, chartHeight-height],
["L", start + barWidth, chartHeight-height],
["L", start + barWidth, chartWidth],
["Z"]
]
}
}
// Add bars
var start = 0;
$.each(values.values, function(index, valueInfo) {
var color = valueInfo.color !== undefined ? valueInfo.color : $.oc.chartUtils.getColor(index),
path = self.paper.path().attr({"stroke-width": 0}).attr({bar: [start, 0]}).attr({fill: color})
self.bars.push(path)
indicators[index].css('background-color', color)
start += barWidth + self.options.gap
path.hover(function(ev){
$.oc.chartUtils.showTooltip(ev.pageX, ev.pageY,
$.trim($.oc.chartUtils.getLegendLabel($legend, index)) + ': <strong>'+valueInfo.value+'</stong>')
}, function() {
$.oc.chartUtils.hideTooltip()
})
})
// Animate bars
start = 0
$.each(values.values, function(index, valueInfo) {
var height = chartHeight/values.max * valueInfo.value;
self.bars[index].animate({bar: [start, height]}, 1000, "bounce")
start += barWidth + self.options.gap;
})
// Update the full-width chart when the window is redized
if (isFullWidth) {
$(window).on('resize', function(){
chartWidth = self.$el.width(),
barWidth = (chartWidth - (values.values.length-1)*self.options.gap)/values.values.length
var start = 0
$.each(values.values, function(index, valueInfo) {
var height = chartHeight/values.max * valueInfo.value;
self.bars[index].animate({bar: [start, height]}, 10, "bounce")
start += barWidth + self.options.gap;
})
})
}
});
}
BarChart.prototype.isFullWidth = function() {
return this.options.fullWidth !== undefined && this.options.fullWidth
}
BarChart.DEFAULTS = {
gap: 2
}
// BARCHART PLUGIN DEFINITION
// ============================
var old = $.fn.barChart
$.fn.barChart = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.barChart')
var options = $.extend({}, BarChart.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data)
$this.data('oc.barChart', (data = new BarChart(this, options)))
})
}
$.fn.barChart.Constructor = BarChart
// BARCHART NO CONFLICT
// =================
$.fn.barChart.noConflict = function () {
$.fn.barChart = old
return this
}
// BARCHART DATA-API
// ===============
$(document).render(function () {
$('[data-control=chart-bar]').barChart()
})
}(window.jQuery);

View File

@ -0,0 +1,239 @@
/*
* Line Chart Plugin
*
* Data attributes:
* - data-control="chart-line" - enables the line chart plugin
* - data-reset-zoom-link="#reset-zoom" - specifies a link to reset zoom
* - data-zoomable - indicates that the chart is zoomable
* - data-time-mode="weeks" - if the "weeks" value is specified and the xaxis mode is "time", the X axis labels will be displayed as week end dates.
* - data-chart-options="xaxis: {mode: 'time'}" - specifies the Flot configuration in JSON format. See https://github.com/flot/flot/blob/master/API.md for details.
*
* Data sets are defined with the SPAN elements inside the chart element: <span data-chart="dataset" data-set-data="[0,0],[1,19]">
* Data set elements could contain data attributes with names in the format "data-set-color". The names for the data set
* attributes are described in the Flot documentation: https://github.com/flot/flot/blob/master/API.md#data-format
*
* JavaScript API:
* $('.chart').chartLine({ resetZoomLink:'#reset-zoom' })
*
*
* Dependences:
* - Flot (jquery.flot.js)
* - Flot Pie (jquery.flot.pie.js)
* - Flot Tooltip (jquery.flot.tooltip.js)
* - Flot Selection (jquery.flot.selection.js)
* - Flot Resize (jquery.flot.resize.js)
* - Flot Time (jquery.flot.time.js)
* - Flot Order Bars (jquery.orderBars.js)
*/
+function ($) { "use strict";
// LINE CHART CLASS DEFINITION
// ============================
var ChartLine = function(element, options) {
var self = this
/*
* Flot options
*/
this.chartOptions = {
xaxis: {
mode: "time",
tickLength: 5
},
selection: { mode: "x" },
grid: {
markingsColor: "rgba(0,0,0, 0.02)",
backgroundColor: { colors: ["#fff", "#fff"] },
borderColor: "#7bafcc",
borderWidth: 0,
color: "#ddd",
hoverable: true,
clickable: true,
labelMargin: 10
},
series: {
lines: {
show: true,
fill: true
},
points: {
show: true
}
},
tooltip: true,
tooltipOpts: {
defaultTheme: false,
content: "%x: <strong>%y</strong>",
dateFormat: "%y-%0m-%0d",
shifts: {
x: 10,
y: 20
}
},
legend: {
show: true,
noColumns: 2
}
}
this.defaultDataSetOptions = {
shadowSize: 0
}
var parsedOptions = {}
try {
parsedOptions = JSON.parse(JSON.stringify(eval("({" + options.chartOptions + "})")));
} catch (e) {
throw new Error('Error parsing the data-chart-options attribute value. '+e);
}
this.chartOptions = $.extend({}, this.chartOptions, parsedOptions)
this.options = options,
this.$el = $(element)
this.fullDataSet = []
this.resetZoomLink = $(options.resetZoomLink)
this.$el.trigger('oc.chartLineInit', [this])
/*
* Bind Events
*/
this.resetZoomLink.on('click', $.proxy(this.clearZoom, this));
if (this.options.zoomable) {
this.$el.on("plotselected", function (event, ranges) {
var newCoords = {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
}
$.plot(self.$el, self.fullDataSet, $.extend(true, {}, self.chartOptions, newCoords))
self.resetZoomLink.show()
});
}
/*
* Markings Helper
*/
if (this.chartOptions.xaxis.mode == "time" && this.options.timeMode == "weeks")
this.chartOptions.markings = weekendAreas
function weekendAreas(axes) {
var markings = [],
d = new Date(axes.xaxis.min);
// Go to the first Saturday
d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
d.setUTCSeconds(0)
d.setUTCMinutes(0)
d.setUTCHours(0)
var i = d.getTime()
do {
// When we don't set yaxis, the rectangle automatically
// extends to infinity upwards and downwards
markings.push({ xaxis: { from: i, to: i + 2 * 24 * 60 * 60 * 1000 } })
i += 7 * 24 * 60 * 60 * 1000
} while (i < axes.xaxis.max)
return markings
}
/*
* Process the datasets
*/
this.initializing = true
this.$el.find('>[data-chart="dataset"]').each(function(){
var data = $(this).data(),
processedData = {};
for (var key in data) {
var normalizedKey = key.substring(3),
value = data[key];
normalizedKey = normalizedKey.charAt(0).toLowerCase() + normalizedKey.slice(1);
if (normalizedKey == 'data')
value = JSON.parse('['+value+']');
processedData[normalizedKey] = value;
}
self.addDataSet($.extend({}, self.defaultDataSetOptions, processedData));
})
/*
* Build chart
*/
this.initializing = false
this.rebuildChart()
}
ChartLine.DEFAULTS = {
chartOptions: "",
timeMode: null,
zoomable: false
}
/*
* Adds a data set to the chart.
* See https://github.com/flot/flot/blob/master/API.md#data-format for the list
* of supported data set options.
*/
ChartLine.prototype.addDataSet = function (dataSet) {
this.fullDataSet.push(dataSet)
if (!this.initializing)
this.rebuildChart()
}
ChartLine.prototype.rebuildChart = function() {
this.$el.trigger('oc.beforeChartLineRender', [this])
$.plot(this.$el, this.fullDataSet, this.chartOptions)
}
ChartLine.prototype.clearZoom = function() {
this.rebuildChart()
this.resetZoomLink.hide()
}
// LINE CHART PLUGIN DEFINITION
// ============================
var old = $.fn.chartLine
$.fn.chartLine = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('october.chartLine')
var options = $.extend({}, ChartLine.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('october.chartLine', (data = new ChartLine(this, options)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.chartLine.Constructor = ChartLine
// LINE CHART NO CONFLICT
// =================
$.fn.chartLine.noConflict = function () {
$.fn.chartLine = old
return this
}
// LINE CHART DATA-API
// ===============
$(document).render(function () {
$('[data-control="chart-line"]').chartLine()
})
}(window.jQuery);

View File

@ -0,0 +1,140 @@
/*
* The pie chart plugin.
*
* Data attributes:
* - data-control="chart-pie" - enables the pie chart plugin
* - data-size="200" - optional, size of the graph
* - data-center-text - text to display inside the graph
*
* JavaScript API:
* $('.scoreboard .chart').pieChart()
*
* Dependences:
* - Raphaël (raphael-min.js)
* - October chart utilities (october.chartutils.js)
*/
+function ($) { "use strict";
var PieChart = function (element, options) {
this.options = options || {};
var
$el = this.$el = $(element),
size = this.size = (this.options.size !== undefined ? this.options.size : $el.height()),
outerRadius = size/2 - 1,
innerRadius = outerRadius - outerRadius/3.5,
total = 0,
values = $.oc.chartUtils.loadListValues($('ul', $el)),
$legend = $.oc.chartUtils.createLegend($('ul', $el)),
indicators = $.oc.chartUtils.initLegendColorIndicators($legend),
self = this;
var $canvas = $('<div/>').addClass('canvas').width(size).height(size)
$el.prepend($canvas)
Raphael($canvas.get(0), size, size, function(){
self.paper = this;
self.segments = this.set()
self.paper.customAttributes.segment = function (startAngle, endAngle) {
var
p1 = self.arcCoords(outerRadius, startAngle),
p2 = self.arcCoords(outerRadius, endAngle),
p3 = self.arcCoords(innerRadius, endAngle),
p4 = self.arcCoords(innerRadius, startAngle),
flag = (endAngle - startAngle) > 180,
path = [
["M", p1.x, p1.y],
["A", outerRadius, outerRadius, 0, +flag, 0, p2.x, p2.y],
["L", p3.x, p3.y],
["A", innerRadius, innerRadius, 0, +flag, 1, p4.x, p4.y],
["Z"]
];
return {path: path}
}
// Draw the background
self.paper.circle(size/2, size/2, innerRadius + (outerRadius - innerRadius)/2)
.attr({"stroke-width": outerRadius - innerRadius - 0.5})
.attr({stroke: $.oc.chartUtils.defaultValueColor})
// Add segments
$.each(values.values, function(index, valueInfo) {
var color = valueInfo.color !== undefined ? valueInfo.color : $.oc.chartUtils.getColor(index),
path = self.paper.path().attr({"stroke-width": 0}).attr({segment: [0, 0]}).attr({fill: color})
self.segments.push(path)
indicators[index].css('background-color', color)
path.hover(function(ev){
$.oc.chartUtils.showTooltip(ev.pageX, ev.pageY,
$.trim($.oc.chartUtils.getLegendLabel($legend, index)) + ': <strong>'+valueInfo.value+'</stong>')
}, function() {
$.oc.chartUtils.hideTooltip()
})
})
// Animate segments
var start = self.options.startAngle;
$.each(values.values, function(index, valueInfo) {
var length = 360/values.total * valueInfo.value;
self.segments[index].animate({segment: [start, start + length]}, 1000, "backOut")
start += length
})
});
if (this.options.centerText !== undefined) {
var $text = $('<span>').addClass('center').html(this.options.centerText)
$canvas.append($text)
}
}
PieChart.prototype.arcCoords = function(radius, angle) {
var
a = Raphael.rad(angle),
x = this.size/2 + radius * Math.cos(a),
y = this.size/2 - radius * Math.sin(a);
return {'x': x, 'y': y}
}
PieChart.DEFAULTS = {
startAngle: 45
}
// PIECHART PLUGIN DEFINITION
// ============================
var old = $.fn.pieChart
$.fn.pieChart = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.pieChart')
var options = $.extend({}, PieChart.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data)
$this.data('oc.pieChart', (data = new PieChart(this, options)))
})
}
$.fn.pieChart.Constructor = PieChart
// PIECHART NO CONFLICT
// =================
$.fn.pieChart.noConflict = function () {
$.fn.pieChart = old
return this
}
// PIECHART DATA-API
// ===============
$(document).render(function () {
$('[data-control=chart-pie]').pieChart()
})
}(window.jQuery);

View File

@ -0,0 +1,109 @@
/*
* October charting utilities.
*/
+function ($) { "use strict";
var ChartUtils = function() {}
ChartUtils.prototype.defaultValueColor = '#b8b8b8';
ChartUtils.prototype.getColor = function(index) {
var
colors = [
'#95b753', '#cc3300', '#e5a91a', '#3366ff', '#ff0f00', '#ff6600',
'#ff9e01', '#fcd202', '#f8ff01', '#b0de09', '#04d215', '#0d8ecf', '#0d52d1',
'#2a0cd0', '#8a0ccf', '#cd0d74', '#754deb', '#dddddd', '#999999', '#333333',
'#000000', '#57032a', '#ca9726', '#990000', '#4b0c25'
],
colorIndex = index % (colors.length-1);
return colors[colorIndex];
}
ChartUtils.prototype.loadListValues = function($list) {
var result = {
values: [],
total: 0,
max: 0
}
$('> li', $list).each(function(){
var value = parseFloat($('span', this).text());
result.total += value
result.values.push({value: value, color: $(this).data('color')})
result.max = Math.max(result.max, value)
})
return result;
}
ChartUtils.prototype.getLegendLabel = function($legend, index) {
return $('tr:eq('+index+') td:eq(1)', $legend).html();
}
ChartUtils.prototype.initLegendColorIndicators = function($legend) {
var indicators = [];
$('tr > td:first-child', $legend).each(function(){
var indicator = $('<i></i>')
$(this).prepend(indicator)
indicators.push(indicator)
})
return indicators;
}
ChartUtils.prototype.createLegend = function($list) {
var
$legend = $('<div>').addClass('chart-legend'),
$table = $('<table>')
$legend.append($table)
$('> li', $list).each(function(){
var label = $(this).clone().children().remove().end().html();
$table.append(
$('<tr>')
.append($('<td class="indicator">'))
.append($('<td>').html(label))
.append($('<td>').addClass('value').html($('span', this).html()))
)
})
$legend.insertAfter($list)
$list.remove()
return $legend;
}
ChartUtils.prototype.showTooltip = function(x, y, text) {
var $tooltip = $('#chart-tooltip')
if ($tooltip.length)
$tooltip.remove()
$tooltip = $('<div id="chart-tooltip">')
.html(text)
.css('visibility', 'hidden')
x += 10
y += 10
$(document.body).append($tooltip)
var tooltipWidth = $tooltip.outerWidth()
if ((x + tooltipWidth) > $(window).width())
x = $(window).width() - tooltipWidth - 10;
$tooltip.css({top: y, left: x, visibility: 'visible'});
}
ChartUtils.prototype.hideTooltip = function() {
$('#chart-tooltip').remove()
}
if ($.oc === undefined)
$.oc = {}
$.oc.chartUtils = new ChartUtils();
}(window.jQuery);

View File

@ -0,0 +1,37 @@
/*
* Custom controls that could exist separately of the form widget
*/
(function($){
$(document).on('keydown', 'div.custom-checkbox', function(e){
if (e.keyCode == 32)
e.preventDefault()
})
$(document).on('keyup', 'div.custom-checkbox', function(e){
if (e.keyCode == 32) {
var $cb = $('input', this)
if ($cb.data('oc-space-timestamp') == e.timeStamp)
return
$cb.get(0).checked = !$cb.get(0).checked
$cb.data('oc-space-timestamp', e.timeStamp)
$cb.trigger('change')
return false
}
})
/*
* Custom drop downs (Desktop only)
*/
$(document).render(function(){
if (Modernizr.touch)
return
$('select.custom-select').select2()
$(document).on('disable', 'select.custom-select', function(event, status){
$(this).select2('enable', !status)
})
})
})(jQuery);

View File

@ -0,0 +1,87 @@
/*
* The loading indicator for the mouse cursor.
*
* Displays the animated loading indicator following the mouse cursor.
*
* JavaScript API:
* $.oc.cursorLoadIndicator.show(event)
* $.oc.cursorLoadIndicator.hide()
*
* By default if the show() method has been called several times, the hide() method should be
* called the same number of times in order to hide the cursor. Use hide(true) to hide the
* indicator forcibly.
*
* The event parameter in the show() method is optional. If it is passed, the initial cursor position
* will be loaded from it.
*/
+function ($) { "use strict";
if ($.oc === undefined)
$.oc = {}
var CursorLoadIndicator = function () {
if (Modernizr.touch)
return
this.counter = 0
this.indicator = $('<div/>').addClass('cursor-loading-indicator').addClass('hide')
$(document.body).append(this.indicator)
}
CursorLoadIndicator.prototype.show = function(event) {
if (Modernizr.touch)
return
this.counter++
if (this.counter > 1)
return
var self = this,
$window = $(window);
if (event !== undefined && event.clientY !== undefined) {
self.indicator.css({
left: event.clientX + 15,
top: event.clientY + 15
})
}
this.indicator.removeClass('hide')
$(window).on('mousemove.cursorLoadIndicator', function(e){
self.indicator.css({
left: e.clientX + 15,
top: e.clientY + 15,
})
})
}
CursorLoadIndicator.prototype.hide = function(force) {
if (Modernizr.touch)
return
this.counter--
if (force !== undefined && force)
this.counter = 0
if (this.counter <= 0) {
this.indicator.addClass('hide')
$(window).off('.cursorLoadIndicator');
}
}
$(document).ready(function(){
$.oc.cursorLoadIndicator = new CursorLoadIndicator();
})
// CURSORLOADINDICATOR DATA-API
// ==============
$(document)
.on('ajaxPromise', '[data-cursor-load-indicator]', function() {
$.oc.cursorLoadIndicator.show()
}).on('ajaxFail ajaxDone', '[data-cursor-load-indicator]', function() {
$.oc.cursorLoadIndicator.hide()
})
}(window.jQuery);

View File

@ -0,0 +1,355 @@
/*
* Allows to scroll an element content in the horizontal or horizontal directions. This script doesn't use
* absolute positioning and rely on the scrollLeft/scrollTop DHTML properties. The element width should be
* fixed with the CSS or JavaScript.
*
* Events triggered on the element:
* - start.oc.dragScroll
* - drag.oc.dragScroll
* - stop.oc.dragScroll
*
* Options:
* - start - callback function to execute when the drag starts
* - drag - callback function to execute when the element is dragged
* - stop - callback function to execute when the drag ends
* - vertical - determines if the scroll direction is vertical, true by default
* - allowScroll - determines if the mouse wheel scrolling is allowed, true by default
* - scrollClassContainer - if specified, specifies an element or element selector to apply the 'scroll-before' and 'scroll-after' CSS classes,
* depending on whether the scrollable area is in its start or end
* - scrollMarkerContainer - if specified, specifies an element or element selector to inject scroll markers (span elements that con
* contain the ellipses icon, indicating whether scrolling is possible)
*
* Methods:
* - isStart - determines if the scrollable area is in its start (left or top)
* - isEnd - determines if the scrollable area is in its end (right or bottom)
* - goToStart - moves the scrollable area to the start (left or top)
* - goToElement - moves the scrollable area to an element
*
* Dependences:
* - Mouse Wheel plugin (mousewheel.js)
*/
+function ($) { "use strict";
var DragScroll = function (element, options) {
this.options = $.extend({}, DragScroll.DEFAULTS, options)
var
$el = $(element),
el = $el.get(0),
dragStart= 0,
startOffset = 0,
self = this,
dragging = false,
eventElementName = this.options.vertical ? 'pageY' : 'pageX';
this.el = $el
this.scrollClassContainer = this.options.scrollClassContainer ? $(this.options.scrollClassContainer) : $el
/*
* Inject scroll markers
*/
if (this.options.scrollMarkerContainer)
$(this.options.scrollMarkerContainer).append($('<span class="before scroll-marker"></span><span class="after scroll-marker"></span>'))
/*
* Bind events
*/
$el.mousewheel(function(event){
if (!self.options.allowScroll)
return;
var offset = self.options.vertical
? ((event.deltaFactor * event.deltaY) * -1)
: ((event.deltaFactor * event.deltaX) * -1)
return !scrollWheel(offset)
})
$el.on('mousedown', function(event){
startDrag(event)
return false
})
$el.on('touchstart', function(event){
var touchEvent = event.originalEvent;
if (touchEvent.touches.length == 1) {
startDrag(touchEvent.touches[0])
event.stopPropagation()
}
})
$el.on('click', function() {
// Do not handle item clicks while dragging
if ($(document.body).hasClass('drag'))
return false
})
$(window).on('resize', $.proxy(this.fixScrollClasses, this))
/*
* Internal event, drag has started
*/
function startDrag(event) {
dragStart = event[eventElementName]
startOffset = self.options.vertical ? $el.scrollTop() : $el.scrollLeft()
if (Modernizr.touch) {
$(window).on('touchmove.dragScroll', function(event){
var touchEvent = event.originalEvent
moveDrag(touchEvent.touches[0])
event.preventDefault()
})
$(window).on('touchend.dragScroll', function(event) {
stopDrag()
})
}
else {
$(window).on('mousemove.dragScroll', function(event){
moveDrag(event)
$(document.body).addClass(self.options.dragClass)
return false
})
$(window).on('mouseup.dragScroll', function(mouseUpEvent){
var isClick = event.pageX == mouseUpEvent.pageX && event.pageY == mouseUpEvent.pageY
stopDrag(isClick)
return false
})
}
}
/*
* Internal event, drag is active
*/
function moveDrag(event) {
var current = event[eventElementName],
offset = dragStart - current
if (Math.abs(offset) > 2) {
if (!dragging) {
dragging = true
$el.trigger('start.oc.dragScroll')
self.options.start();
}
self.options.vertical
? $el.scrollTop(startOffset + offset)
: $el.scrollLeft(startOffset + offset)
$el.trigger('drag.oc.dragScroll')
self.options.drag()
}
}
/*
* Internal event, drag has ended
*/
function stopDrag(click) {
$(window).off('.dragScroll')
dragging = false;
if (click)
$(document.body).removeClass(self.options.dragClass)
else
self.fixScrollClasses()
window.setTimeout(function(){
if (!click) {
$(document.body).removeClass(self.options.dragClass)
$el.trigger('stop.oc.dragScroll')
self.options.stop()
self.fixScrollClasses()
}
}, 100)
}
/*
* Scroll wheel has moved by supplied offset
*/
function scrollWheel(offset) {
startOffset = self.options.vertical ? el.scrollTop : el.scrollLeft
self.options.vertical
? $el.scrollTop(startOffset + offset)
: $el.scrollLeft(startOffset + offset)
var scrolled = self.options.vertical
? el.scrollTop != startOffset
: el.scrollLeft != startOffset
$el.trigger('drag.oc.dragScroll')
self.options.drag()
if (scrolled) {
if (self.wheelUpdateTimer !== undefined && self.wheelUpdateTimer !== false)
window.clearInterval(self.wheelUpdateTimer);
self.wheelUpdateTimer = window.setTimeout(function() {
self.wheelUpdateTimer = false;
self.fixScrollClasses()
}, 100);
}
return scrolled
}
this.fixScrollClasses();
}
DragScroll.DEFAULTS = {
vertical: false,
allowScroll: true,
scrollClassContainer: false,
scrollMarkerContainer: false,
dragClass: 'drag',
start: function() {},
drag: function() {},
stop: function() {}
}
DragScroll.prototype.fixScrollClasses = function() {
this.scrollClassContainer.toggleClass('scroll-before', !this.isStart())
this.scrollClassContainer.toggleClass('scroll-after', !this.isEnd())
this.scrollClassContainer.toggleClass('scroll-active-before', this.isActiveBefore())
this.scrollClassContainer.toggleClass('scroll-active-after', this.isActiveAfter())
}
DragScroll.prototype.isStart = function() {
if (!this.options.vertical)
return this.el.scrollLeft() <= 0;
else
return this.el.scrollTop() <= 0;
}
DragScroll.prototype.isEnd = function() {
if (!this.options.vertical)
return (this.el[0].scrollWidth - (this.el.scrollLeft() + this.el.width())) <= 0
else
return (this.el[0].scrollHeight - (this.el.scrollTop() + this.el.height())) <= 0
}
DragScroll.prototype.goToStart = function() {
if (!this.options.vertical)
return this.el.scrollLeft(0)
else
return this.el.scrollTop(0)
}
/*
* Determines if the element with the class 'active' is hidden before the viewport -
* on the left or on the top, depending on whether the scrollbar is horizontal or vertical.
*/
DragScroll.prototype.isActiveAfter = function() {
var activeElement = $('.active', this.el);
if (activeElement.length == 0)
return false
if (!this.options.vertical)
return activeElement.get(0).offsetLeft > (this.el.scrollLeft() + this.el.width())
else
return activeElement.get(0).offsetTop > (this.el.scrollTop() + this.el.height())
}
/*
* Determines if the element with the class 'active' is hidden after the viewport -
* on the right or on the bottom, depending on whether the scrollbar is horizontal or vertical.
*/
DragScroll.prototype.isActiveBefore = function() {
var activeElement = $('.active', this.el);
if (activeElement.length == 0)
return false
if (!this.options.vertical)
return (activeElement.get(0).offsetLeft + activeElement.width()) < this.el.scrollLeft()
else
return (activeElement.get(0).offsetTop + activeElement.height()) < this.el.scrollTop()
}
DragScroll.prototype.goToElement = function(element, callback) {
var $el = $(element)
if (!$el.length)
return;
var self = this,
params = {
duration: 300,
queue: false,
complete: function(){
self.fixScrollClasses()
if (callback !== undefined)
callback()
}
}
var offset = 0,
animated = false
if (!this.options.vertical) {
offset = $el.get(0).offsetLeft - this.el.scrollLeft()
if (offset < 0) {
this.el.animate({'scrollLeft': $el.get(0).offsetLeft}, params)
animated = true
} else {
offset = $el.get(0).offsetLeft + $el.width() - (this.el.scrollLeft() + this.el.width())
if (offset > 0) {
this.el.animate({'scrollLeft': $el.get(0).offsetLeft + $el.width() - this.el.width()}, params)
animated = true
}
}
} else {
offset = $el.get(0).offsetTop - this.el.scrollTop()
if (offset < 0) {
this.el.animate({'scrollTop': $el.get(0).offsetTop}, params)
animated = true
} else {
offset = $el.get(0).offsetTop - (this.el.scrollTop() + this.el.height())
if (offset > 0) {
this.el.animate({'scrollTop': $el.get(0).offsetTop + $el.height() - this.el.height()}, params)
animated = true
}
}
}
if (!animated && callback !== undefined)
callback()
}
// DRAGSCROLL PLUGIN DEFINITION
// ============================
var old = $.fn.dragScroll
$.fn.dragScroll = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.dragScroll')
var options = typeof option == 'object' && option
if (!data) $this.data('oc.dragScroll', (data = new DragScroll(this, options)))
if (typeof option == 'string') {
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
})
}
$.fn.dragScroll.Constructor = DragScroll
// DRAGSCROLL NO CONFLICT
// =================
$.fn.dragScroll.noConflict = function () {
$.fn.dragScroll = old
return this
}
}(window.jQuery);

View File

@ -0,0 +1,102 @@
/*
* Dropdown menus.
*
* This script customizes the Twitter Bootstrap drop-downs.
*
*/
+function ($) { "use strict";
$(document).on('shown.bs.dropdown', '.dropdown', function(){
$(document.body).addClass('dropdown-open')
var dropdown = $('.dropdown-menu', this),
dropdownContainer = $(this).data('dropdown-container')
if ($('.dropdown-container', dropdown).length == 0) {
var
title = $('[data-toggle=dropdown]', this).text(),
titleAttr = dropdown.data('dropdown-title'),
timer = null;
if (titleAttr !== undefined)
title = titleAttr
$('li:first-child', dropdown).addClass('first-item')
dropdown.prepend($('<li/>').addClass('dropdown-title').text(title))
var
container = $('<li/>').addClass('dropdown-container'),
ul = $('<ul/>')
container.prepend(ul)
ul.prepend(dropdown.children())
dropdown.prepend(container)
dropdown.on('touchstart', function(){
window.setTimeout(function(){
dropdown.addClass('scroll')
}, 200)
})
dropdown.on('touchend', function(){
window.setTimeout(function(){
dropdown.removeClass('scroll')
}, 200)
})
dropdown.on('click', 'a', function(){
if (dropdown.hasClass('scroll'))
return false
})
}
if (dropdownContainer !== undefined && dropdownContainer == 'body') {
$(this).data('oc.dropdown', dropdown)
$(document.body).append(dropdown)
dropdown.css({
'visibility': 'hidden',
'left': 0,
'top' : 0,
'display': 'block'
})
var targetOffset = $(this).offset(),
targetHeight = $(this).height(),
targetWidth = $(this).width(),
position = {
x: targetOffset.left,
y: targetOffset.top + targetHeight
},
leftOffset = targetWidth < 30 ? -16 : 0,
documentHeight = $(document).height(),
dropdownHeight = dropdown.height()
if ((dropdownHeight + position.y) > $(document).height()) {
position.y = targetOffset.top - dropdownHeight - 12
dropdown.addClass('top')
} else
dropdown.removeClass('top')
dropdown.css({
'left': position.x + leftOffset,
'top': position.y,
'visibility': 'visible'
})
}
if ($('.dropdown-overlay', document.body).length == 0)
$(document.body).prepend($('<div/>').addClass('dropdown-overlay'));
})
$(document).on('hidden.bs.dropdown', '.dropdown', function(){
var dropdown = $(this).data('oc.dropdown')
if (dropdown !== undefined) {
dropdown.css('display', 'none')
$(this).append(dropdown)
}
$(document.body).removeClass('dropdown-open');
})
}(window.jQuery);

View File

@ -0,0 +1,158 @@
/*
* File List
*
* Creates a tree list of clickable folders and files.
*
* Data attributes:
* - data-control="filelist" - enables the file list plugin
* - data-group-status-handler - AJAX handler to execute when a group is collapsed or expanded by a user
*
* JavaScript API:
* $('#list').fileList()
*
* Events
* - open.oc.list - this event is triggered on the list element when an item is clicked.
*
* Dependences:
* - Null
*/
+function ($) { "use strict";
// FILELIST CLASS DEFINITION
// ============================
var FileList = function(element, options) {
this.options = options
this.$el = $(element)
this.init();
}
FileList.DEFAULTS = {
}
FileList.prototype.init = function (){
var self = this
this.$el.on('click', 'li.group > h4 > a, li.group > div.group', function() {
self.toggleGroup($(this).closest('li'))
return false;
});
this.$el.on('click', 'li.item > a', function(event) {
var e = $.Event('open.oc.list', {relatedTarget: $(this).parent().get(0), clickEvent: event})
self.$el.trigger(e, this)
return false
})
}
FileList.prototype.toggleGroup = function(group) {
var $group = $(group);
$group.attr('data-status') == 'expanded' ?
this.collapseGroup($group) :
this.expandGroup($group)
}
FileList.prototype.collapseGroup = function(group) {
var
$list = $('> ul, > div.subitems', group),
self = this;
$list.css('overflow', 'hidden')
$list.animate({'height': 0}, { duration: 100, queue: false, complete: function() {
$list.css({
'overflow': 'visible',
'display': 'none'
})
$(group).attr('data-status', 'collapsed')
$(window).trigger('resize')
} })
this.sendGroupStatusRequest(group, 0);
}
FileList.prototype.expandGroup = function(group) {
var
$list = $('> ul, > div.subitems', group),
self = this;
$list.css({
'overflow': 'hidden',
'display': 'block',
'height': 0
})
$list.animate({'height': $list[0].scrollHeight}, { duration: 100, queue: false, complete: function() {
$list.css({
'overflow': 'visible',
'height': 'auto'
})
$(group).attr('data-status', 'expanded')
$(window).trigger('resize')
} })
this.sendGroupStatusRequest(group, 1);
}
FileList.prototype.sendGroupStatusRequest = function(group, status) {
if (this.options.groupStatusHandler !== undefined) {
var groupId = $(group).data('group-id')
if (groupId === undefined)
groupId = $('> h4 a', group).text();
$(group).request(this.options.groupStatusHandler, {data: {group: groupId, status: status}})
}
}
FileList.prototype.markActive = function(dataId) {
$('li.item', this.$el).removeClass('active')
if (dataId)
$('li.item[data-id="'+dataId+'"]', this.$el).addClass('active')
}
// FILELIST PLUGIN DEFINITION
// ============================
var old = $.fn.fileList
$.fn.fileList = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.fileList')
var options = $.extend({}, FileList.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.fileList', (data = new FileList(this, options)))
if (typeof option == 'string') {
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
})
}
$.fn.fileList.Constructor = FileList
// FILELIST NO CONFLICT
// =================
$.fn.fileList.noConflict = function () {
$.fn.fileList = old
return this
}
// FILELIST DATA-API
// ===============
$(document).ready(function () {
$('[data-control=filelist]').fileList()
})
}(window.jQuery);

View File

@ -0,0 +1,13 @@
/*
* Filter Behavior
*
* Data attributes:
* - data-behavior="filter" - enables the filter plugin
*
* JavaScript API:
* $('a#someLink').filterBehavior()
*
* Dependences:
* - October Popover (october.popover.js)
*/

View File

@ -0,0 +1,68 @@
/*
* The flash message.
*
* The default hide interval is 2 seconds. The interval option is not required.
*
* Data attributes API:
* <p data-control="flash-message" class="success" data-interval="5">The record has been successfully saved.</p>
*
* JavaScript API:
* $.oc.flashMsg({text: 'The record has been successfully saved.', 'class': 'success', 'interval': 3})
*/
+function ($) { "use strict";
var FlashMessage = function (options, el) {
var
options = $.extend({}, FlashMessage.DEFAULTS, options),
$element = $(el);
$('body > p.flash-message').remove()
if ($element.length == 0)
$element = $('<p/>').addClass(options.class).html(options.text)
$element.addClass('flash-message')
$element.attr('data-control', null)
$element.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>')
$element.on('click', 'button', remove)
$(document.body).append($element);
var timer = window.setTimeout(remove, options.interval*1000)
function remove() {
window.clearInterval(timer)
$element.animate({'opacity': 0}, {
duration: 200,
queue: false,
complete: function() {
$element.remove();
}
})
}
}
FlashMessage.DEFAULTS = {
class: 'success',
text: 'Default text',
interval: 2
}
// FLASH MESSAGE PLUGIN DEFINITION
// ============================
if ($.oc === undefined)
$.oc = {}
$.oc.flashMsg = FlashMessage
// FLASH MESSAGE DATA-API
// ===============
$(document).render(function(){
$('[data-control=flash-message]').each(function(){
$.oc.flashMsg($(this).data(), this)
})
})
}(window.jQuery);

View File

@ -0,0 +1,77 @@
/*
* The goal meter plugin.
*
* Applies the goal meter style to a scoreboard item.
*
* Data attributes:
* - data-control="goal-meter" - enables the goal meter plugin
* - data-value - sets the value, in percents
*
* JavaScript API:
* $('.scoreboard .goal-meter').goalMeter({value: 20})
* $('.scoreboard .goal-meter').goalMeter(10) // Sets the current value
*/
+function ($) { "use strict";
var GoalMeter = function (element, options) {
var
$el = this.$el = $(element),
self = this;
this.options = options || {};
this.$indicatorBar = $('<span/>').text(this.options.value + '%')
this.$indicatorOuter = $('<span/>').addClass('goal-meter-indicator').append(this.$indicatorBar)
$('p', this.$el).first().before(this.$indicatorOuter)
window.setTimeout(function(){
self.update(self.options.value)
}, 200)
}
GoalMeter.prototype.update = function(value) {
this.$indicatorBar.css('height', value + '%')
}
GoalMeter.DEFAULTS = {
value: 50
}
// GOALMETER PLUGIN DEFINITION
// ============================
var old = $.fn.goalMeter
$.fn.goalMeter = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.goalMeter')
var options = $.extend({}, GoalMeter.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data)
$this.data('oc.goalMeter', (data = new GoalMeter(this, options)))
else
data.update(option)
})
}
$.fn.goalMeter.Constructor = GoalMeter
// GOALMETER NO CONFLICT
// =================
$.fn.goalMeter.noConflict = function () {
$.fn.goalMeter = old
return this
}
// GOALMETER DATA-API
// ===============
$(document).render(function () {
$('[data-control=goal-meter]').goalMeter()
})
}(window.jQuery);

View File

@ -0,0 +1,136 @@
/*
* Hot key binding.
*
* JavaScript API:
*
* $('html').hotKey({ hotkey: 'Ctrl+S', hotkeyMac: 'Command+M', callback: doSomething);
*/
+function ($) { "use strict";
var HotKey = function (element, options) {
var $el = this.$el = $(element)
var $target = this.$target = $(options.hotkeyTarget)
this.options = options || {}
if (!options.hotkey)
throw new Error('No hotkey has been defined.');
if (!options.hotkeyMac)
options.hotkeyMac = options.hotkey
var
keys,
keysCount,
platform = (navigator.userAgent.indexOf('Mac OS X') != -1) ? 'hotkeyMac' : 'hotkey',
keyBind = options[platform].toLowerCase(),
keyPressed = { shift: false, ctrl: false, cmd: false, alt: false },
keyWaited = { shift: false, ctrl: false, cmd: false, alt: false, specific: -1 },
keyMap = {'esc':27, 'tab':9, 'space':32, 'return':13, 'enter':13, 'backspace':8, 'scroll':145, 'capslock':20, 'numlock':144, 'pause':19,
'break':19, 'insert':45, 'home':36, 'delete':46, 'suppr':46, 'end':35, 'pageup':33, 'pagedown':34, 'left':37, 'up':38, 'right':39, 'down':40,
'f1':112, 'f2':113, 'f3':114, 'f4':115, 'f5':116, 'f6':117, 'f7':118, 'f8':119, 'f9':120, 'f10':121, 'f11':122, 'f12':123}
keys = keyBind.split('+')
keysCount = keys.length;
for (var i = 0; i < keysCount; i++) {
switch (keys[i]) {
case 'shift':
keyWaited.shift = true
break
case 'ctrl':
keyWaited.ctrl = true
break
case 'command':
case 'cmd':
keyWaited.cmd = true
break
case 'alt':
keyWaited.alt = true
break
}
}
keyWaited.specific = keyMap[keys[keys.length-1]]
if (typeof (keyWaited.specific) == 'undefined')
keyWaited.specific = keys[keys.length-1].toUpperCase().charCodeAt()
$target.keydown(function (event) {
keyPressed.shift = event.originalEvent.shiftKey
keyPressed.ctrl = event.originalEvent.ctrlKey
keyPressed.cmd = event.originalEvent.metaKey
keyPressed.alt = event.originalEvent.altKey
if (event.which == keyWaited.specific
&& keyPressed.shift == keyWaited.shift
&& keyPressed.ctrl == keyWaited.ctrl
&& keyPressed.cmd == keyWaited.cmd
&& keyPressed.alt == keyWaited.alt) {
if (options.hotkeyVisible && !$el.is(':visible'))
return
if (options.callback)
return options.callback($el, this)
keyPressed.shift = false
keyPressed.ctrl = false
keyPressed.cmd = false
keyPressed.alt = false
}
});
$target.keyup(function (event) {
keyPressed.shift = event.originalEvent.shiftKey
keyPressed.ctrl = event.originalEvent.ctrlKey
keyPressed.cmd = event.originalEvent.metaKey
keyPressed.alt = event.originalEvent.altKey
});
}
HotKey.DEFAULTS = {
hotkey: null,
hotkeyMac: null,
hotkeyTarget: 'html',
hotkeyVisible: true,
callback: function(element) {
element.trigger('click')
return false
}
}
// HOTKEY PLUGIN DEFINITION
// ============================
var old = $.fn.hotKey
$.fn.hotKey = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.hotkey')
var options = $.extend({}, HotKey.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.hotkey', (data = new HotKey(this, options)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.hotKey.Constructor = HotKey
// HOTKEY NO CONFLICT
// =================
$.fn.hotKey.noConflict = function () {
$.fn.hotKey = old
return this
}
// HOTKEY DATA-API
// ==============
$(document).render(function(){
$('[data-hotkey]').hotKey()
})
}(window.jQuery);

View File

@ -0,0 +1,98 @@
/*
* An input preset converter.
*
* The API allows to convert text entered into an element to an URL or file name value in another input element.
*
* Supported data attributes:
* - data-input-preset: specifies a CSS selector for a source input element
* - data-input-preset-closest-parent: optional, specifies a CSS selector for a closest common parent
* for the source and destination input elements.
* - data-input-preset-type: specifies the conversion type. Supported values are: URL, file.
*
* Example: <input type="text" id="name" value=""/>
* <input type="text"
* data-input-preset="#name"
* data-input-preset-type="file">
*
* JavaScript API:
* $('#filename').inputPreset({inputPreset: '#name', inputPresetType: 'file'})
*/
+function ($) { "use strict";
var InputPreset = function (element, options) {
var $el = this.$el = $(element);
this.options = options || {};
this.cancelled = false;
// Do not update the element if it already has a value
if ($el.val().length)
return
var parent = options.inputPresetClosestParent !== undefined ?
$el.closest(options.inputPresetClosestParent) :
undefined,
self = this;
this.$src = $(options.inputPreset, parent),
this.$src.on('keyup', function() {
if (self.cancelled)
return;
$el.val(self.formatValue())
})
this.$el.on('change', function() {
self.cancelled = true
})
}
InputPreset.prototype.formatValue = function() {
var value = this.$src.val()
.toLowerCase()
.replace(/[^\w ]+/g,'')
.replace(/ +/g,'-');
if (this.options.inputPresetType == 'url')
value = '/' + value;
return value;
}
InputPreset.DEFAULTS = {
inputPreset: '',
inputPresetType: 'file',
inputPresetClosestParent: undefined
}
// INPUT CONVERTER PLUGIN DEFINITION
// ============================
var old = $.fn.inputPreset
$.fn.inputPreset = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.inputPreset')
var options = $.extend({}, InputPreset.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.inputPreset', (data = new InputPreset(this, options)))
})
}
$.fn.inputPreset.Constructor = InputPreset
// INPUT CONVERTER NO CONFLICT
// =================
$.fn.inputPreset.noConflict = function () {
$.fn.inputPreset = old
return this
}
// INPUT CONVERTER DATA-API
// ===============
$(document).render(function(){
$('[data-input-preset]').inputPreset()
})
}(window.jQuery);

View File

@ -0,0 +1,624 @@
/*
* Inspector control
*
* Manages properties of inspectable controls.
*
* Data attributes:
* - data-inspectable - makes a control inspectable.
* - data-inspector-title - title for the inspector popup
* - data-inspector-description - optional description for the inspector popup
* - data-inspector-class - class name of the inspectable object. Required for drop-down
* fields with dynamic options.
* - data-inspector-container - specifies a CSS selector for the inspector container. The default container
* is the document body. The container element should be relative positioned. The 'self' container value
* allows to inject the inspector to the inspectable element.
* - data-inspector-offset - offset in pixels to add to the calculated position, to make the position more "random"
* - data-inspector-placement - top | bottom | left | right. The placement could automatically be changed
if the popover doesn't fit into the desired position.
* - data-inspector-fallback-placement - The placement to use if the default placement
* and all other possible placements do not work. The default value is "bottom".
* - data-inspector-config - Configuration of the inspector fields as an array in the JSON format.
* Each element in the array is an object with the following properties:
* - property
* - title
* - type (currently supported types are: string, checkbox, dropdown)
* - description (optional)
* - validationPattern (regex pattern for for validating the value, supported by the text editor)
* - validationMessage (a message to display if the validation fails)
* - placeholder - placholder text, for text and dropdown properties
* - depends - a list of properties the property depend on, for dropdown lists
* - options - an option list for dropdown lists, optional. If not provided the options are loaded with AJAX.
* Example of the configuration string (a single property):
* [{"property":"max-width","title":"Max width","type":"string"}]
*
* The Inspector requires the inspectable element to contain a hidden input element with the attribute "data-inspector-values".
* The inspector uses this field to read and write field values. The field value is a JSON string, an object with keys matching property
* names and values matching property values.
*
* Events
* - change - the event is triggered on the inspectable element when it's properties are updated.
* - showing.oc.inspector - triggered before the Inspector is displayed. Allows to cancel the action with e.preventDefault()
* - hiding.oc.inspector - triggered before the Inspector is hidden. Allows to cancel the action with e.preventDefault()
* - hidden.oc.inspector - triggered after the Inspector is hidden.
*
* Dependences:
* - october.popover.js
*/
+function ($) { "use strict";
if ($.oc === undefined)
$.oc = {}
$.oc.inspector = {
editors: {},
propertyCounter: 0
}
// INSPECTOR CLASS DEFINITION
// ============================
var Inspector = function(element, options) {
this.options = options
this.$el = $(element)
this.loadConfiguration()
}
Inspector.prototype.loadConfiguration = function() {
var jsonString = this.$el.data('inspector-config')
if (jsonString === undefined)
throw new Error('The Inspector cannot be initialized because the Inspector configuration ' +
'attribute is not defined on the inspectable element.');
if (!$.isArray(jsonString)) {
try {
this.config = $.parseJSON(jsonString)
} catch(err) {
throw new Error('Error parsing the Inspector field configuration. ' + err)
}
} else
this.config = jsonString
this.propertyValuesField = $('input[data-inspector-values]', this.$el)
if (this.propertyValuesField.length === 0)
throw new Error('Error initializing the Inspector: the inspectable element should contain ' +
'an hidden input element with the data-inspector-values property.')
}
Inspector.prototype.getPopoverTemplate = function() {
return ' \
<div class="popover-head"> \
<h3>{{title}}</h3> \
{{#description}} \
<p>{{description}}</p> \
{{/description}} \
<button type="button" class="close" \
data-dismiss="popover" \
aria-hidden="true">&times;</button> \
</div> \
<form> \
<table class="inspector-fields"> \
{{#properties}} \
<tr id="{{#propFormat}}{{property}}{{/propFormat}}"> \
<th><div title="{{title}}"> \
{{title}} \
{{#info}}{{/info}} \
</div></th> \
{{#editor}}{{/editor}} \
</tr> \
{{/properties}} \
</table> \
<form> \
'
}
Inspector.prototype.init = function() {
var self = this,
data = {
title: this.$el.data('inspector-title'),
description: this.$el.data('inspector-description'),
properties: this.config,
editor: function() {
return function(text, render) {
return self.renderEditor(this, render)
}
},
info: function() {
return function(text, render) {
if (this.description !== undefined && this.description != null)
return render('<span data-toggle="tooltip" title="{{description}}" class="info oc-icon-info"></span>', this)
}
},
propFormat: function() {
return function(text, render) {
return 'prop-'+render(text).replace('.', '-')
}
}
}
this.editors = []
this.initProperties()
this.$el.data('oc.inspectorVisible', true)
var displayPopover = function() {
var offset = self.$el.data('inspector-offset')
if (offset === undefined)
offset = 15
var offsetX = self.$el.data('inspector-offset-x'),
offsetY = self.$el.data('inspector-offset-y')
var placement = self.$el.data('inspector-placement')
if (placement === undefined)
placement = 'bottom'
var fallbackPlacement = self.$el.data('inspector-fallback-placement')
if (fallbackPlacement === undefined)
fallbackPlacement = 'bottom'
self.$el.ocPopover({
content: Mustache.render(self.getPopoverTemplate(), data),
highlightModalTarget: true,
modal: true,
placement: placement,
fallbackPlacement: fallbackPlacement,
containerClass: 'control-inspector',
container: self.$el.data('inspector-container'),
offset: offset,
offsetX: offsetX,
offsetY: offsetY,
width: 300
})
self.$el.on('hiding.oc.popover', function(e){self.onBeforeHide(e)})
self.$el.on('hide.oc.popover', function(){self.cleanup()})
self.$el.addClass('inspector-open')
$(self.$el.data('oc.popover').$container).on('keydown', function(e){
if(e.keyCode == 13)
$(this).trigger('close.oc.popover')
})
if (self.editors.length > 0) {
if (self.editors[0].focus !== undefined)
self.editors[0].focus()
}
$.each(self.editors, function(){
if (this.init !== undefined)
this.init()
})
$('[data-toggle=tooltip]', self.$el.data('oc.popover').$container).tooltip({placement: 'auto right', container: 'body'})
}
var e = $.Event('showing.oc.inspector')
this.$el.trigger(e, [{callback: displayPopover}])
if (e.isDefaultPrevented())
return
if (!e.isPropagationStopped())
displayPopover()
}
Inspector.prototype.initProperties = function() {
var propertyValuesStr = $.trim(this.propertyValuesField.val())
try {
this.propertyValues = propertyValuesStr.length === 0 ? {} : $.parseJSON(propertyValuesStr)
this.originalPropertyValues = $.extend(true, {}, this.propertyValues)
} catch(err) {
throw new Error('Error parsing the Inspector property values string. ' + err)
}
}
Inspector.prototype.readProperty = function(property) {
if (this.propertyValues[property] !== undefined)
return this.propertyValues[property]
return null
}
Inspector.prototype.writeProperty = function(property, value) {
this.propertyValues[property] = value
this.propertyValuesField.val(JSON.stringify(this.propertyValues))
if (this.originalPropertyValues[property] === undefined || this.originalPropertyValues[property] != value) {
this.$el.trigger('change')
this.markPropertyChanged(property, true)
} else
this.markPropertyChanged(property, false)
this.$el.trigger('propertyChanged.oc.Inspector', [property])
return value
}
Inspector.prototype.markPropertyChanged = function(property, changed) {
$('#prop-'+property.replace('.', '-'), this.$el.data('oc.popover').$container).toggleClass('changed', changed)
}
Inspector.prototype.renderEditor = function(data, render) {
$.oc.inspector.propertyCounter ++
var editorClass = 'inspectorEditor'
+ data.type.charAt(0).toUpperCase()
+ data.type.slice(1),
editorId = 'inspector-property-'+data.type+$.oc.inspector.propertyCounter
if ($.oc.inspector.editors[editorClass] === undefined)
throw new Error('The Inspector editor class "' + editorClass +
'" is not defined in the $.oc.inspector.editors namespace.')
var editor = new $.oc.inspector.editors[editorClass](editorId, this, data)
this.editors.push(editor)
return editor.renderEditor()
}
Inspector.prototype.cleanup = function() {
this.$el.off('hiding.oc.popover')
this.$el.off('hide.oc.popover')
this.$el.off('.oc.Inspector')
this.$el.removeClass('inspector-open')
var e = $.Event('hidden.oc.inspector')
this.$el.trigger(e)
this.$el.data('oc.inspectorVisible', false)
}
Inspector.prototype.onBeforeHide = function(e) {
$.each(this.editors, function() {
this.applyValue()
})
var eH = $.Event('hiding.oc.inspector')
this.$el.trigger(eH, [{values: this.propertyValues}])
if (eH.isDefaultPrevented()) {
e.preventDefault()
return false
}
$.each(this.editors, function() {
if (this.validate === undefined)
return true
var validationError = this.validate()
if (!validationError)
return true
alert(validationError)
e.preventDefault()
var self = this
setTimeout(function (){
self.focus()
}, 0)
return false
})
var $contianer = this.$el.data('inspector-container')
$('[data-toggle=tooltip]', this.$el.data('oc.popover').$container).tooltip('hide')
}
//
// EDITOR DEFINITIONS
// ==================
/*
* Inspector editor classes should be defined in the $.oc.inspector.editors namespace.
* Editors could be defined in other scripts. The methods editors should
* define are:
* - renderEditor(), the editor cell HTML
* - validate(), optional, validates the value and returns the validation error message
* - applyValue(), applies the editor value
* - focus(), focuses the editor input element, if applicable
*/
// STRING EDITOR
// ==================
var InspectorEditorString = function(editorId, inspector, fieldDef) {
this.inspector = inspector
this.fieldDef = fieldDef
this.editorId = editorId
this.selector = '#'+this.editorId+' input'
var self = this
$(document).on('focus', this.selector, function() {
var $field = $(this)
$('td', $field.closest('table')).removeClass('active')
$field.closest('td').addClass('active')
})
$(document).on('change', this.selector, function() {
self.applyValue()
})
}
InspectorEditorString.prototype.applyValue = function() {
this.inspector.writeProperty(this.fieldDef.property, $.trim($(this.selector).val()))
}
InspectorEditorString.prototype.renderEditor = function() {
var data = {
id: this.editorId,
value: $.trim(this.inspector.readProperty(this.fieldDef.property)),
placeholder: this.fieldDef.placeholder !== undefined ? this.fieldDef.placeholder : ''
}
return Mustache.render('<td class="text" id="{{id}}"><input type="text" value="{{value}}" placeholder="{{placeholder}}"/></td>', data)
}
InspectorEditorString.prototype.validate = function() {
if (this.fieldDef.validationPattern === undefined)
return
var val = $.trim($(this.selector).val()),
re = new RegExp(this.fieldDef.validationPattern, 'm')
if (!val.match(re))
return this.fieldDef.validationMessage
}
InspectorEditorString.prototype.focus = function() {
$(this.selector).focus()
}
$.oc.inspector.editors.inspectorEditorString = InspectorEditorString;
// CHECKBOX EDITOR
// ==================
var InspectorEditorCheckbox = function(editorId, inspector, fieldDef) {
this.inspector = inspector
this.fieldDef = fieldDef
this.editorId = editorId
this.selector = '#'+this.editorId+' input'
var self = this
$(document).on('change', this.selector, function() {
self.applyValue()
})
}
InspectorEditorCheckbox.prototype.applyValue = function() {
this.inspector.writeProperty(this.fieldDef.property, $(this.selector).get(0).checked ? 1 : 0 )
}
InspectorEditorCheckbox.prototype.renderEditor = function() {
var data = {
id: this.editorId,
checked: this.inspector.readProperty(this.fieldDef.property) ? 'checked' : null,
cbId: this.editorId + '-cb',
title: this.fieldDef.title
}
return Mustache.render(this.getTemplate(), data)
}
InspectorEditorCheckbox.prototype.focus = function() {
$(this.selector).closest('div').focus()
}
InspectorEditorCheckbox.prototype.getTemplate = function() {
return ' \
<td id="{{id}}"> \
<div tabindex="0" class="checkbox \
custom-checkbox nolabel"> \
<input type="checkbox" \
value="1" \
{{checked}} id="{{cbId}}"/> \
<label for="{{cbId}}">{{title}}</label> \
</div> \
</td> \
';
}
$.oc.inspector.editors.inspectorEditorCheckbox = InspectorEditorCheckbox;
// DROPDOWN EDITOR
// ==================
var InspectorEditorDropdown = function(editorId, inspector, fieldDef) {
this.inspector = inspector
this.fieldDef = fieldDef
this.editorId = editorId
this.selector = '#'+this.editorId+' select'
this.dynamicOptions = this.fieldDef.options ? false : true
var self = this
$(document).on('change', this.selector, function() {
self.applyValue()
})
}
InspectorEditorDropdown.prototype.applyValue = function() {
this.inspector.writeProperty(this.fieldDef.property, $(this.selector).val())
}
InspectorEditorDropdown.prototype.renderEditor = function() {
var
self = this,
data = {
id: this.editorId,
value: $.trim(this.inspector.readProperty(this.fieldDef.property)),
selectId: this.editorId + '-select',
defaultOption: function() {
return function(text, render) {
if (self.fieldDef.placeholder == undefined)
return ''
if (!Modernizr.touch)
return '<option></option>'
}
}
}
if (this.fieldDef.options) {
var options = []
if (this.fieldDef.placeholder !== undefined && Modernizr.touch)
options.push({value: null, title: this.fieldDef.placeholder})
$.each(this.fieldDef.options, function(value, title){
options.push({value: value, title: title})
})
data.options = options
}
return Mustache.render(this.getTemplate(), data)
}
InspectorEditorDropdown.prototype.getTemplate = function() {
return ' \
<td id="{{id}}" class="dropdown"> \
<select id="{{selectId}}" class="custom-select"> \
{{#defaultOption}}{{/defaultOption}} \
{{#options}} \
<option value="{{value}}"> \
{{title}} \
</option> \
{{/options}} \
</select> \
</td> \
';
}
InspectorEditorDropdown.prototype.init = function() {
var value = this.inspector.readProperty(this.fieldDef.property),
self = this
$(this.selector).val(value)
if (!Modernizr.touch) {
var options = {
dropdownCssClass: 'ocInspectorDropdown'
}
if (this.fieldDef.placeholder !== undefined)
options.placeholder = this.fieldDef.placeholder
$(this.selector).select2(options)
}
if (this.dynamicOptions) {
if (!Modernizr.touch) {
this.indicatorContainer = $('.select2-container', $(this.selector).closest('td'))
this.indicatorContainer.addClass('loading-indicator-container').addClass('size-small').addClass('transparent')
}
this.loadOptions()
}
if (this.fieldDef.depends)
this.inspector.$el.on('propertyChanged.oc.Inspector', $.proxy(this.onDependencyChanged, this))
}
InspectorEditorDropdown.prototype.onDependencyChanged = function(ev, property) {
if ($.inArray(property, this.fieldDef.depends) === -1)
return
var self = this,
dependencyValues = this.getDependencyValues()
if (this.prevDependencyValues === undefined || this.prevDependencyValues != dependencyValues)
this.loadOptions()
}
InspectorEditorDropdown.prototype.saveDependencyValues = function() {
this.prevDependencyValues = this.getDependencyValues()
}
InspectorEditorDropdown.prototype.getDependencyValues = function() {
var dependencyValues = '',
self = this
$.each(this.fieldDef.depends, function(index, masterProperty){
dependencyValues += masterProperty + ':' + self.inspector.readProperty(masterProperty) + '-'
})
return dependencyValues
}
InspectorEditorDropdown.prototype.showLoadingIndicator = function() {
if (!Modernizr.touch)
this.indicatorContainer.loadIndicator({'opaque': true})
}
InspectorEditorDropdown.prototype.hideLoadingIndicator = function() {
if (!Modernizr.touch)
this.indicatorContainer.loadIndicator('hide')
}
InspectorEditorDropdown.prototype.loadOptions= function() {
var $form = $(this.selector).closest('form'),
data = this.inspector.propertyValues,
$select = $(this.selector),
currentValue = this.inspector.readProperty(this.fieldDef.property),
self = this
if (this.fieldDef.depends)
this.saveDependencyValues()
data.inspectorProperty = this.fieldDef.property
data.inspectorClassName = this.inspector.options.inspectorClass
this.showLoadingIndicator()
$form.request('onInspectableGetOptions', {
data: data,
success: function(data) {
$('option', $select).remove()
if (self.fieldDef.placeholder !== undefined)
$select.append($('<option></option>'))
if (data.options)
$.each(data.options, function(value, title) {
$select.append($('<option></option>').attr('value', value).text(title))
})
var hasOption = $('option[value="' + currentValue + '"]', $select).length > 0
if (hasOption)
$select.val(currentValue)
else
$('option:first-child', $select).attr("selected", "selected");
$select.trigger('change')
self.hideLoadingIndicator()
},
error: function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
self.hideLoadingIndicator()
}
})
}
$.oc.inspector.editors.inspectorEditorDropdown = InspectorEditorDropdown;
// INSPECTOR DATA-API
// ==================
$(document).on('click', '[data-inspectable]', function(){
var $this = $(this),
inspector = $this.data('oc.inspector')
if ($this.data('oc.inspectorVisible'))
return false
if (inspector === undefined) {
inspector = new Inspector(this, $this.data())
$this.data('oc.inspector', inspector)
}
inspector.init()
return false
})
}(window.jQuery);

View File

@ -0,0 +1,19 @@
(function($){
function updateLayout() {
$('.layout-cell.width-fix').each(function(){
var $el = $(this).children();
if ($el.length > 0) {
var margin = $el.data('oc.layoutMargin');
if (margin === undefined) {
margin = parseInt($el.css('marginRight')) + parseInt($el.css('marginLeft'))
$el.data('oc.layoutMargin', margin)
}
$(this).width($el.get(0).offsetWidth + margin)
}
})
}
$(document).ready(updateLayout)
$(window).on('resize', updateLayout)
})(jQuery);

View File

@ -0,0 +1,123 @@
/*
* The loading indicator.
*
* The load indicator DIV is injected inside its container. The container should have
* the relative position (use the loading-indicator-container class for it).
*
* Used with framework.js
*
* data-load-indicator="Message" - displays a load indicator with a supplied message, the element
* must be wrapped in a `<div class="loading-indicator-container"></div>` container.
*
* JavaScript API:
*
* $('#buttons').loadIndicator({text: 'Saving...', 'opaque': true}) - display the indicator
* $('#buttons').loadIndicator('hide') - display the indicator
*/
+function ($) { "use strict";
var LoadIndicator = function (element, options) {
var $el = this.$el = $(element)
this.options = options || {}
this.tally = 0
this.show()
}
LoadIndicator.prototype.hide = function() {
this.tally--
if (this.tally <= 0) {
$('div.loading-indicator', this.$el).remove()
this.$el.removeClass('in-progress')
}
}
LoadIndicator.prototype.show = function(options) {
if (options)
this.options = options
this.hide()
var indicator = $('<div class="loading-indicator"></div>')
indicator.append($('<div></div>').text(this.options.text))
indicator.append($('<span></span>'))
if (this.options.opaque == undefined)
indicator.addClass('transparent')
this.$el.prepend(indicator)
this.$el.addClass('in-progress')
this.tally++
}
LoadIndicator.DEFAULTS = {
text: ''
}
// LOADINDICATOR PLUGIN DEFINITION
// ============================
var old = $.fn.loadIndicator
$.fn.loadIndicator = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.loadIndicator')
var options = $.extend({}, LoadIndicator.DEFAULTS, typeof option == 'object' && option)
if (!data) {
if (typeof option == 'string')
return;
$this.data('oc.loadIndicator', (data = new LoadIndicator(this, options)))
} else {
if (typeof option !== 'string')
data.show(options);
else {
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
}
})
}
$.fn.loadIndicator.Constructor = LoadIndicator
// LOADINDICATOR NO CONFLICT
// =================
$.fn.loadIndicator.noConflict = function () {
$.fn.loadIndicator = old
return this
}
// LOADINDICATOR DATA-API
// ==============
$(document)
.on('ajaxPromise', '[data-load-indicator]', function() {
var
indicatorContainer = $(this).closest('.loading-indicator-container'),
loadingText = $(this).data('load-indicator'),
options = {
opaque: $(this).data('load-indicator-opaque')
}
if (loadingText)
options.text = loadingText
indicatorContainer.loadIndicator(options)
})
.on('ajaxFail ajaxDone', '[data-load-indicator]', function() {
$(this).closest('.loading-indicator-container').loadIndicator('hide')
})
}(window.jQuery);

View File

@ -0,0 +1,22 @@
/*
* Top navigation bar. Features of the bar:
* - Hide content if the display width is less than 768px. In this case the menu icon is displayed.
* When the icon is clicked, the menu content is displayed on the left side of the page.
* - If the content doesn't fit the navbar, it can be dragged left and right.
*
* Dependences:
* - DragScroll (october.dragscroll.js)
* - VerticalMenu (october.verticalmenu.js)
*/
(function($){
$(window).load(function() {
$('nav.navbar').each(function(){
var
navbar = $(this),
nav = $('ul.nav', navbar)
nav.verticalMenu($('a.menu-toggle', navbar))
})
})
})(jQuery);

View File

@ -0,0 +1,350 @@
/*
* Popover plugin
*
* Options:
* - placement: top | bottom | left | right. The placement could automatically be changed
if the popover doesn't fit into the desired position.
* - fallbackPlacement: top | bottom | left | right. The placement to use if the default placement
* and all other possible placements do not work. The default value is "bottom".
* - content: content HTML string or callback
* - width: content width, optional. If not specified, the content width will be used.
* - modal: make the popover modal
* - highlightModalTarget: "pop" the popover target above the overlay, making it highlighted.
* The feature assigns the target position relative.
* - closeOnPageClick: close the popover if the page was clicked outside the popover area.
* - container: the popover container selector or element. The default container is the document body.
* The container must be relative positioned.
* - containerClass - a CSS class to apply to the popover container element
* - offset - offset in pixels to add to the calculated position, to make the position more "random"
* - offsetX - X offset in pixels to add to the calculated position, to make the position more "random".
* If specified, overrides the offset property for the bottom and top popover placement.
* - offsetY - Y offset in pixels to add to the calculated position, to make the position more "random".
* If specified, overrides the offset property for the left and right popover placement.
*
* Methods:
* - hide
*
* Closing the popover. There are 3 ways to close the popover: call it's hide() method, trigger
* the close.oc.popover on any element inside the popover or click an element with attribute
* data-dismiss="popover" inside the popover.
*
* Events:
* - showing.oc.popover - triggered before the popover is displayed. Allows to override the
* popover options (for example the content) or cancel the action with e.preventDefault()
* - hiding.oc.popover - triggered before the popover is closed. Allows to cancel the action with
* e.preventDefault()
* - hide.oc.popover - triggered after the popover is hidden.
*
* JavaScript API:
* $('#element').ocPopover({
content: '<p>This is a popover</p>'
placement: 'top'
* })
*/
+function ($) { "use strict";
var Popover = function (element, options) {
var $el = this.$el = $(element);
this.options = options || {};
this.arrowSize = 15
this.show()
}
Popover.prototype.hide = function() {
var e = $.Event('hiding.oc.popover', {relatedTarget: this.$el})
this.$el.trigger(e, this)
if (e.isDefaultPrevented())
return
if (this.$container) this.$container.remove()
if (this.$overlay) this.$overlay.remove()
this.$overlay = false;
this.$container = false;
this.$el.removeClass('popover-highlight')
this.$el.data('oc.popover', null)
$(document.body).removeClass('popover-open')
$(document).unbind('mousedown', this.docClickHandler);
this.$el.trigger('hide.oc.popover')
$(document).off('.oc.popover');
}
Popover.prototype.show = function(options) {
var self = this;
/*
* Trigger the show event
*/
var e = $.Event('showing.oc.popover', {relatedTarget: this.$el})
this.$el.trigger(e, this)
if (e.isDefaultPrevented())
return
/*
* Create the popover container and overlay
*/
this.$container = $('<div/>')
.addClass('control-popover')
.css('visibility', 'hidden')
if (this.options.containerClass)
this.$container.addClass(this.options.containerClass)
var $content = $('<div/>').html(this.getContent())
this.$container.append($content)
if (this.options.width)
this.$container.width(this.options.width)
if (this.options.modal) {
this.$overlay = $('<div/>').addClass('popover-overlay')
$(document.body).append(this.$overlay)
if (this.options.highlightModalTarget) {
this.$el.addClass('popover-highlight')
this.$el.blur()
}
} else
this.$overlay = false
if (this.options.container)
$(this.options.container).append(this.$container);
else
$(document.body).append(this.$container);
/*
* Determine the popover position
*/
var
placement = this.calcPlacement(),
position = this.calcPosition(placement);
this.$container.css({
left: position.x,
top: position.y
}).addClass('placement-'+placement)
/*
* Display the popover
*/
this.$container.css('visibility', 'visible')
$(document.body).addClass('popover-open')
/*
* Bind events
*/
this.$container.on('mousedown', function(e){
e.stopPropagation();
})
this.$container.on('close.oc.popover', function(e){
self.hide()
})
this.$container.on('click', '[data-dismiss=popover]', function(e){
self.hide()
return false;
})
this.docClickHandler = $.proxy(this.onDocumentClick, this)
$(document).bind('mousedown', this.docClickHandler);
if (this.options.closeOnEsc) {
$(document).on('keyup.oc.popover', function(e){
if (e.keyCode == 27) {
self.hide()
return false;
}
})
}
}
Popover.prototype.getContent = function () {
return typeof this.options.content == 'function' ?
this.options.content.call(this.$el[0], this) :
this.options.content
}
Popover.prototype.calcDimensions = function() {
var
documentWidth = $(document).width(),
documentHeight = $(document).height(),
targetOffset = this.$el.offset(),
targetWidth = this.$el.outerWidth(),
targetHeight = this.$el.outerHeight();
return {
containerWidth: this.$container.outerWidth() + this.arrowSize,
containerHeight: this.$container.outerHeight() + this.arrowSize,
targetOffset: targetOffset,
targetHeight: targetHeight,
targetWidth: targetWidth,
spaceLeft: targetOffset.left,
spaceRight: documentWidth - (targetWidth + targetOffset.left),
spaceTop: targetOffset.top,
spaceBottom: documentHeight - (targetHeight + targetOffset.top),
spaceHorizontalBottom: documentHeight - targetOffset.top,
spaceVerticalRight: documentWidth - targetOffset.left
}
}
Popover.prototype.fitsLeft = function(dimensions) {
return dimensions.spaceLeft >= dimensions.containerWidth &&
dimensions.spaceHorizontalBottom >= dimensions.containerHeight
}
Popover.prototype.fitsRight = function(dimensions) {
return dimensions.spaceRight >= dimensions.containerWidth &&
dimensions.spaceHorizontalBottom >= dimensions.containerHeight
}
Popover.prototype.fitsBottom = function(dimensions) {
return dimensions.spaceBottom >= dimensions.containerHeight &&
dimensions.spaceVerticalRight >= dimensions.containerWidth
}
Popover.prototype.fitsTop = function(dimensions) {
return dimensions.spaceTop >= dimensions.containerHeight &&
dimensions.spaceVerticalRight >= dimensions.containerWidth
}
Popover.prototype.calcPlacement = function() {
var
placement = this.options.placement,
dimensions = this.calcDimensions();
if (placement != 'bottom' && placement != 'top' && placement != 'left' && placement != 'right')
placement = 'bottom'
var placementFunctions = {
top: this.fitsTop,
bottom: this.fitsBottom,
left: this.fitsLeft,
right: this.fitsRight
}
if (placementFunctions[placement](dimensions))
return placement
for (var index in placementFunctions) {
if (placementFunctions[index](dimensions))
return index
}
return this.options.fallbackPlacement
}
Popover.prototype.calcPosition = function(placement) {
var
dimensions = this.calcDimensions(),
result;
switch (placement) {
case 'left' :
var realOffset = this.options.offsetY === undefined ? this.options.offset : this.options.offsetY
result = {x: (dimensions.targetOffset.left - dimensions.containerWidth), y: dimensions.targetOffset.top + realOffset}
break;
case 'top' :
var realOffset = this.options.offsetX === undefined ? this.options.offset : this.options.offsetX
result = {x: dimensions.targetOffset.left + realOffset, y: (dimensions.targetOffset.top - dimensions.containerHeight)}
break;
case 'bottom' :
var realOffset = this.options.offsetX === undefined ? this.options.offset : this.options.offsetX
result = {x: dimensions.targetOffset.left + realOffset, y: (dimensions.targetOffset.top + dimensions.targetHeight + this.arrowSize)}
break;
case 'right' :
var realOffset = this.options.offsetY === undefined ? this.options.offset : this.options.offsetY
result = {x: (dimensions.targetOffset.left + dimensions.targetWidth + this.arrowSize), y: dimensions.targetOffset.top + realOffset}
break;
}
if (!this.options.container)
return result
var
$container = $(this.options.container),
containerOffset = $container.offset();
result.x -= containerOffset.left;
result.y -= containerOffset.top;
return result;
}
Popover.prototype.onDocumentClick = function() {
if (this.options.closeOnPageClick)
this.hide();
}
Popover.DEFAULTS = {
placement: 'bottom',
fallbackPlacement: 'bottom',
content: '<p>Popover content<p>',
width: false,
modal: false,
highlightModalTarget: false,
closeOnPageClick: true,
closeOnEsc: true,
container: false,
containerClass: null,
offset: 15
}
// POPOVER PLUGIN DEFINITION
// ============================
var old = $.fn.ocPopover
$.fn.ocPopover = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.popover')
var options = $.extend({}, Popover.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) {
if (typeof option == 'string')
return;
$this.data('oc.popover', (data = new Popover(this, options)))
} else {
if (typeof option != 'string')
return;
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
})
}
$.fn.ocPopover.Constructor = Popover
// POPOVER NO CONFLICT
// =================
$.fn.ocPopover.noConflict = function () {
$.fn.ocPopover = old
return this
}
// POPOVER DATA-API
// ===============
$(document).on('click', '[data-control=popover]', function(e){
$(this).ocPopover()
return false;
})
}(window.jQuery);

View File

@ -0,0 +1,268 @@
/*
* Ajax Popup plugin
*
* Data attributes:
* - data-control="popup" - enables the ajax popup plugin
* - data-ajax="popup-content.htm" - ajax content to load
* - data-handler="widget:pluginName" - October ajax request name
* - data-keyboard="false" - Allow popup to be closed with the keyboard
* - data-request-data="file_id: 1" - October ajax request data
*
* JavaScript API:
* $('a#someLink').popup({ ajax: 'popup-content.htm' })
* $('a#someLink').popup({ handler: 'onOpenPopupForm' })
*
* Dependences:
* - Bootstrap Modal (modal.js)
*/
+function ($) { "use strict";
// POPUP CLASS DEFINITION
// ============================
var Popup = function(element, options) {
var self = this
this.options = options
this.$el = $(element)
this.$target = null
this.$modal = null
this.$backdrop = null
this.isOpen = false
this.isAjax = false
this.firstDiv = null
this.allowHide = true
this.$target = this.createPopupContainer()
this.$content = this.$target.find('.modal-content:first')
this.$modal = this.$target.modal({ show: false, backdrop: false, keyboard: this.options.keyboard })
this.isAjax = this.options.handler || this.options.ajax
/*
* Hook in to BS Modal events
*/
this.$modal.on('hide.bs.modal', function(){
self.isOpen = false
self.setBackdrop(false)
if (self.isAjax) {
// Wait for animation to complete
setTimeout(function() { self.$content.empty() }, 500)
}
})
this.$modal.on('show.bs.modal', function(){
self.isOpen = true
self.setBackdrop(true)
})
this.$modal.on('close.oc.popup', function(){
self.$modal.modal('hide')
return false
})
this.init()
}
Popup.DEFAULTS = {
ajax: null,
handler: null,
keyboard: true,
extraData: {}
}
Popup.prototype.init = function(){
var self = this
/*
* Do not allow the same popup to open twice
*/
if (self.isOpen)
return
/*
* Show loading panel
*/
this.setBackdrop(true)
this.setLoading(true)
/*
* October AJAX
*/
if (this.options.handler) {
this.$el.request(this.options.handler, {
data: this.options.extraData,
success: function(data, textStatus, jqXHR) {
self.setContent(data.result)
$(window).trigger('ajaxUpdateComplete', [this, data, textStatus, jqXHR])
self.triggerEvent('popupComplete')
},
error: function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
self.hide()
self.triggerEvent('popupError')
}
})
}
/*
* Regular AJAX
*/
else if (this.options.ajax) {
$.ajax({
url: this.options.ajax,
data: this.options.extraData,
success: function(data) {
self.setContent(data)
},
cache: false
});
}
}
Popup.prototype.createPopupContainer = function() {
var
modal = $('<div />').prop({
class: 'control-popup modal fade',
role: 'dialog',
tabindex: -1
}),
modalDialog = $('<div />').addClass('modal-dialog'),
modalContent = $('<div />').addClass('modal-content')
return modal.append(modalDialog.append(modalContent))
}
Popup.prototype.setContent = function(contents) {
this.show()
this.setLoading(false)
this.$content.html(contents)
// Duplicate the popup object reference on to the first div
// inside the popup. Eg: $('#firstDiv').popup('hide')
this.firstDiv = this.$content.find('>div:first')
if (this.firstDiv.length > 0)
this.firstDiv.data('oc.popup', this)
}
Popup.prototype.setBackdrop = function(val) {
if (val && !this.$backdrop) {
this.$backdrop = $('<div class="popup-backdrop fade" />')
.appendTo(document.body)
this.$backdrop.addClass('in')
this.$backdrop.append($('<div class="popup-loading-indicator modal-content" />'))
}
else if (!val && this.$backdrop) {
this.$backdrop.remove()
this.$backdrop = null;
}
}
Popup.prototype.setLoading = function(val) {
if (!this.$backdrop)
return;
var self = this;
if (val) {
setTimeout(function(){ self.$backdrop.addClass('loading'); }, 100)
}
else {
this.$backdrop.removeClass('loading');
}
}
Popup.prototype.triggerEvent = function(eventName, params) {
if (!params)
params = [this.$el, this.$modal]
this.$el.trigger(eventName, params)
if (this.firstDiv)
this.firstDiv.trigger(eventName, params)
}
Popup.prototype.reload = function() {
this.init()
}
Popup.prototype.show = function() {
this.$modal.on('click.dismiss.popup', '[data-dismiss="popup"]', $.proxy(this.hide, this))
this.triggerEvent('popupShow')
this.$modal.modal('show')
}
Popup.prototype.hide = function() {
this.triggerEvent('popupHide')
if (this.allowHide)
this.$modal.modal('hide')
}
/*
* Hide the popup without destroying it,
* you should call .hide() once finished
*/
Popup.prototype.visible = function(val) {
if (val)
this.$modal.addClass('in')
else
this.$modal.removeClass('in')
this.setBackdrop(val)
}
Popup.prototype.toggle = function() {
this.triggerEvent('popupToggle', [this.$modal])
this.$modal.modal('toggle')
}
/*
* Lock the popup from closing
*/
Popup.prototype.lock = function(val) {
this.allowHide = !val
}
// POPUP PLUGIN DEFINITION
// ============================
var old = $.fn.popup
$.fn.popup = function (option) {
var args = Array.prototype.slice.call(arguments, 1)
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.popup')
var options = $.extend({}, Popup.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.popup', (data = new Popup(this, options)))
else if (typeof option == 'string') data[option].apply(data, args)
else data.reload()
})
}
$.fn.popup.Constructor = Popup
// POPUP NO CONFLICT
// =================
$.fn.popup.noConflict = function () {
$.fn.popup = old
return this
}
// POPUP DATA-API
// ===============
$(document).on('click.oc.popup', '[data-control="popup"]', function() {
$(this).popup()
return false
});
}(window.jQuery);

View File

@ -0,0 +1,91 @@
/*
* Table row linking plugin
*
* Data attributes:
* - data-control="rowlink" - enables the plugin on an element
*
* JavaScript API:
* $('a#someElement').rowLink()
*
* Dependences:
* - Null
*/
+function ($) { "use strict";
// ROWLINK CLASS DEFINITION
// ============================
var RowLink = function(element, options) {
var self = this
this.options = options
this.$el = $(element)
var tr = this.$el.prop('tagName') == 'TR'
? this.$el
: this.$el.find('tr:has(td)')
tr.each(function(){
var link = $(this).find(options.target).filter(function(){
return !$(this).closest('td').hasClass(options.excludeClass)
}).first()
if (!link.length) return
var href = link.attr('href'),
onclick = (typeof link.get(0).onclick == "function") ? link.get(0).onclick : null
$(this).find('td').not('.' + options.excludeClass).click(function() {
if (onclick)
onclick.apply(link.get(0))
else
window.location = href;
})
$(this).addClass(options.linkedClass)
link.replaceWith(link.html())
})
}
RowLink.DEFAULTS = {
target: 'a',
excludeClass: 'nolink',
linkedClass: 'rowlink'
}
// ROWLINK PLUGIN DEFINITION
// ============================
var old = $.fn.rowLink
$.fn.rowLink = function (option) {
var args = Array.prototype.slice.call(arguments, 1)
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.rowlink')
var options = $.extend({}, RowLink.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.rowlink', (data = new RowLink(this, options)))
else if (typeof option == 'string') data[option].apply(data, args)
})
}
$.fn.rowLink.Constructor = RowLink
// ROWLINK NO CONFLICT
// =================
$.fn.rowLink.noConflict = function () {
$.fn.rowLink = old
return this
}
// ROWLINK DATA-API
// ===============
$(document).render(function() {
$('[data-control="rowlink"]').rowLink()
})
}(window.jQuery);

View File

@ -0,0 +1,290 @@
/*
* Creates a scrollbar in a container.
*
* Note the element must have a height set for vertical,
* and a width set for horizontal.
*
*
* Data attributes:
* - data-control="scrollbar" - enables the scrollbar plugin
*
* JavaScript API:
* $('#area').scrollbar()
*
* Dependences:
* - Mouse Wheel plugin (mousewheel.js)
*/
+function ($) { "use strict";
var Scrollbar = function (element, options) {
var
$el = this.$el = $(element),
el = $el.get(0),
self = this,
options = this.options = options || {},
sizeName = this.sizeName = options.vertical ? 'height' : 'width',
isTouch = this.isTouch = Modernizr.touch,
isScrollable = this.isScrollable = false,
isLocked = this.isLocked = false,
eventElementName = options.vertical ? 'pageY' : 'pageX',
dragStart = 0,
startOffset = 0;
/*
* Create Scrollbar
*/
this.$scrollbar = $('<div />').addClass('scrollbar-scrollbar')
this.$track = $('<div />').addClass('scrollbar-track').appendTo(this.$scrollbar)
this.$thumb = $('<div />').addClass('scrollbar-thumb').appendTo(this.$track)
$el
.addClass('drag-scrollbar')
.addClass(options.vertical ? 'vertical' : 'horizontal')
.prepend(this.$scrollbar)
/*
* Bind events
*/
if (isTouch) {
this.$el.on('touchstart', function (event){
var touchEvent = event.originalEvent;
if (touchEvent.touches.length == 1) {
startDrag(touchEvent.touches[0])
event.stopPropagation()
}
})
}
else {
this.$thumb.on('mousedown', function (event){
startDrag(event)
})
this.$track.on('mouseup', function (event){
moveDrag(event)
})
}
$el.mousewheel(function (event){
var offset = self.options.vertical
? ((event.deltaFactor * event.deltaY) * -1)
: ((event.deltaFactor * event.deltaX) * -1)
return !scrollWheel(offset * self.options.scrollSpeed)
})
$el.on('oc.scrollbar.gotoStart', function(event){
self.options.vertical
? $el.scrollTop(0)
: $el.scrollLeft(0)
self.update()
event.stopPropagation()
})
$(window).on('resize', $.proxy(this.update, this))
/*
* Internal event, drag has started
*/
function startDrag(event) {
$('body').addClass('drag-noselect')
dragStart = event[eventElementName]
startOffset = self.options.vertical ? $el.scrollTop() : $el.scrollLeft()
if (isTouch) {
$(window).on('touchmove.scrollbar', function(event) {
var touchEvent = event.originalEvent
if (moveDrag(touchEvent.touches[0]))
event.preventDefault();
});
$el.on('touchend.scrollbar', stopDrag)
}
else {
$(window).on('mousemove.scrollbar', function(event){
moveDrag(event)
return false
})
$(window).on('mouseup.scrollbar', function(){
stopDrag()
return false
})
}
}
/*
* Internal event, drag is active
*/
function moveDrag(event) {
self.isLocked = true;
var
offset,
dragTo = event[eventElementName]
// Touch devices use an inverse scrolling interface
// with a 1:1 ratio
if (self.isTouch) {
offset = dragStart - dragTo
}
// Mouse devices use a natural scrolling interface
// with a track:canvas ratio
else {
var ratio = self.getCanvasSize() / self.getViewportSize()
offset = (dragTo - dragStart) * ratio
}
self.options.vertical
? $el.scrollTop(startOffset + offset)
: $el.scrollLeft(startOffset + offset)
self.setThumbPosition()
return self.options.vertical
? el.scrollTop != startOffset
: el.scrollLeft != startOffset
}
/*
* Internal event, drag has ended
*/
function stopDrag() {
$('body').removeClass('drag-noselect')
$(window).off('.scrollbar')
}
/*
* Scroll wheel has moved by supplied offset
*/
function scrollWheel(offset) {
startOffset = self.options.vertical ? el.scrollTop : el.scrollLeft
self.options.vertical
? $el.scrollTop(startOffset + offset)
: $el.scrollLeft(startOffset + offset)
var scrolled = self.options.vertical
? el.scrollTop != startOffset
: el.scrollLeft != startOffset
self.setThumbPosition()
return scrolled
}
/*
* Give the DOM a second, then set the track and thumb size
*/
setTimeout(function() { self.update() }, 1);
}
Scrollbar.DEFAULTS = {
vertical: true,
scrollSpeed: 2,
start: function() {},
drag: function() {},
stop: function() {}
}
Scrollbar.prototype.update = function() {
this.$scrollbar.hide()
this.setThumbSize()
this.setThumbPosition()
this.$scrollbar.show()
}
Scrollbar.prototype.setThumbSize = function() {
var properties = this.calculateProperties()
this.isScrollable = !(properties.thumbSizeRatio >= 1);
this.$scrollbar.toggleClass('disabled', !this.isScrollable)
if (this.options.vertical) {
this.$track.height(properties.canvasSize)
this.$thumb.height(properties.thumbSize)
}
else {
this.$track.width(properties.canvasSize)
this.$thumb.width(properties.thumbSize)
}
}
Scrollbar.prototype.setThumbPosition = function() {
var properties = this.calculateProperties()
if (this.options.vertical)
this.$thumb.css({top: properties.thumbPosition})
else
this.$thumb.css({left: properties.thumbPosition})
}
Scrollbar.prototype.calculateProperties = function() {
var $el = this.$el,
properties = {};
properties.viewportSize = this.getViewportSize()
properties.canvasSize = this.getCanvasSize()
properties.scrollAmount = (this.options.vertical) ? $el.scrollTop() : $el.scrollLeft()
properties.thumbSizeRatio = properties.viewportSize / properties.canvasSize
properties.thumbSize = properties.viewportSize * properties.thumbSizeRatio
properties.thumbPositionRatio = properties.scrollAmount / (properties.canvasSize - properties.viewportSize)
properties.thumbPosition = ((properties.viewportSize - properties.thumbSize) * properties.thumbPositionRatio) + properties.scrollAmount
if (isNaN(properties.thumbPosition))
properties.thumbPosition = 0
return properties;
}
Scrollbar.prototype.getViewportSize = function() {
return (this.options.vertical)
? this.$el.height()
: this.$el.width();
}
Scrollbar.prototype.getCanvasSize = function() {
return (this.options.vertical)
? this.$el.get(0).scrollHeight
: this.$el.get(0).scrollWidth;
}
// SCROLLBAR PLUGIN DEFINITION
// ============================
var old = $.fn.scrollbar
$.fn.scrollbar = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.scrollbar')
var options = $.extend({}, Scrollbar.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.scrollbar', (data = new Scrollbar(this, options)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.scrollbar.Constructor = Scrollbar
// SCROLLBAR NO CONFLICT
// =================
$.fn.scrollbar.noConflict = function () {
$.fn.scrollbar = old
return this
}
// SCROLLBAR DATA-API
// ===============
$(document).render(function(){
$('[data-control=scrollbar]').scrollbar()
})
}(window.jQuery);

View File

@ -0,0 +1,117 @@
/*
* Side Navigation
*
* Data attributes:
* - data-control="sidenav" - enables the side navigation plugin
*
* JavaScript API:
* $('#nav').sideNav()
* $.oc.sideNav.setCounter('cms/partials', 5); - sets the counter value for a particular menu item
* $.oc.sideNav.increaseCounter('cms/partials', 5); - increases the counter value for a particular menu item
* $.oc.sideNav.dropCounter('cms/partials'); - drops the counter value for a particular menu item
*
* Dependences:
* - Drag Scroll (october.dragscroll.js)
*/
+function ($) { "use strict";
if ($.oc === undefined)
$.oc = {}
// SIDENAV CLASS DEFINITION
// ============================
var SideNav = function(element, options) {
this.options = options
this.$el = $(element)
this.$list = $('ul', this.$el)
this.init();
}
SideNav.DEFAULTS = {
}
SideNav.prototype.init = function (){
var self = this;
this.$list.dragScroll({
vertical: true,
start: function(){self.$list.addClass('drag')},
stop: function(){self.$list.removeClass('drag')},
scrollClassContainer: self.$el,
scrollMarkerContainer: self.$el
})
this.$list.on('click', function() {
/* Do not handle menu item clicks while dragging */
if (self.$list.hasClass('drag'))
return false
})
}
SideNav.prototype.setCounter = function (itemId, value){
var $counter = $('span.counter[data-menu-id="'+itemId+'"]', this.$el)
$counter.removeClass('empty')
$counter.toggleClass('empty', value == 0)
$counter.text(value)
return this
}
SideNav.prototype.increaseCounter = function (itemId, value){
var $counter = $('span.counter[data-menu-id="'+itemId+'"]', this.$el)
var originalValue = parseInt($counter.text())
if (isNaN(originalValue))
originalValue = 0
var newValue = value + originalValue
$counter.toggleClass('empty', newValue == 0)
$counter.text(newValue)
return this
}
SideNav.prototype.dropCounter = function (itemId){
this.setCounter(itemId, 0)
return this
}
// SIDENAV PLUGIN DEFINITION
// ============================
var old = $.fn.sideNav
$.fn.sideNav = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.sideNav')
var options = $.extend({}, SideNav.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.sideNav', (data = new SideNav(this, options)))
if (typeof option == 'string') data[option].call($this)
if ($.oc.sideNav === undefined)
$.oc.sideNav = data
})
}
$.fn.sideNav.Constructor = SideNav
// SIDENAV NO CONFLICT
// =================
$.fn.sideNav.noConflict = function () {
$.fn.sideNav = old
return this
}
// SIDENAV DATA-API
// ===============
$(document).ready(function(){
$('[data-control="sidenav"]').sideNav()
})
}(window.jQuery);

View File

@ -0,0 +1,194 @@
/*
* Side Panel Tabs
*/
+function ($) { "use strict";
var SidePanelTab = function(element, options) {
this.options = options
this.$el = $(element)
this.init()
}
SidePanelTab.prototype.init = function() {
var self = this
this.$sideNavItems = $('#layout-sidenav ul li')
this.$sidePanelItems = $('[data-content-id]', this.$el)
this.sideNavWidth = $('#layout-sidenav ul li').outerWidth()
this.mainNavHeight = $('#layout-mainmenu').outerHeight()
this.panelVisible = false
this.visibleItemId = false
this.$fixButton = $('<a href="#" class="fix-button"><i class="icon-thumb-tack"></i></a>')
this.$fixButton.click(function(){
self.fixPanel()
return false
})
$('.fix-button-container', this.$el).append(this.$fixButton)
this.$sideNavItems.click(function(){
if (Modernizr.touch && $(window).width() < self.options.breakpoint) {
if ($(this).data('menu-item') == self.visibleItemId && self.panelVisible) {
self.hideSidePanel()
return
} else
self.displaySidePanel()
}
self.displayTab(this)
return false
})
if (!Modernizr.touch) {
$('#layout-sidenav').mouseenter(function(){
if ($(window).width() < self.options.breakpoint || !self.panelFixed())
self.displaySidePanel()
})
self.$el.mouseleave(function(){
self.hideSidePanel()
})
self.$sideNavItems.mouseenter(function(){
if ($(window).width() < self.options.breakpoint || !self.panelFixed())
self.displayTab(this)
})
$(window).resize(function() {
self.updatePanelPosition()
self.updateActiveTab()
})
} else {
$('#layout-body').click(function(){
if (self.panelVisible) {
self.hideSidePanel()
return false
}
})
self.$el.on('close.oc.sidePanel', function(){
self.hideSidePanel()
})
}
this.updateActiveTab()
}
SidePanelTab.prototype.displayTab = function(menuItem) {
var menuItemId = $(menuItem).data('menu-item')
this.$sideNavItems.removeClass('active')
$(menuItem).addClass('active')
this.visibleItemId = menuItemId
this.$sidePanelItems.each(function(){
var $el = $(this)
$el.toggleClass('hide', $el.data('content-id') != menuItemId)
})
$(window).trigger('resize')
}
SidePanelTab.prototype.displaySidePanel = function() {
$(document.body).addClass('display-side-panel')
this.$el.appendTo('#layout-canvas')
this.panelVisible = true
this.$el.css({
left: this.sideNavWidth,
top: this.mainNavHeight
})
this.updatePanelPosition()
$(window).trigger('resize')
}
SidePanelTab.prototype.hideSidePanel = function() {
$(document.body).removeClass('display-side-panel')
if (this.$el.next('#layout-body').length == 0)
$('#layout-body').before(this.$el)
this.panelVisible = false
this.updateActiveTab()
}
SidePanelTab.prototype.updatePanelPosition = function() {
this.$el.height($(document).height() - this.mainNavHeight)
if (this.panelVisible && $(window).width() > this.options.breakpoint && this.panelFixed())
this.hideSidePanel()
}
SidePanelTab.prototype.updateActiveTab = function() {
if (!this.panelVisible && ($(window).width() < this.options.breakpoint || !this.panelFixed()))
this.$sideNavItems.removeClass('active')
else {
this.$sideNavItems.filter('[data-menu-item='+this.visibleItemId+']').addClass('active')
}
}
SidePanelTab.prototype.panelFixed = function() {
return !$(document.body).hasClass('side-panel-not-fixed')
}
SidePanelTab.prototype.fixPanel = function() {
$(document.body).toggleClass('side-panel-not-fixed')
var fixed = this.panelFixed()
fixed
? this.updateActiveTab()
: this.hideSidePanel()
if (typeof(localStorage) !== 'undefined')
localStorage.ocSidePanelFixed = fixed ? 1 : 0
}
SidePanelTab.DEFAULTS = {
breakpoint: 769
}
// PLUGIN DEFINITION
// ============================
var old = $.fn.sidePanelTab
$.fn.sidePanelTab = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.sidePanelTab')
var options = $.extend({}, SidePanelTab.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.sidePanelTab', (data = new SidePanelTab(this, options)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.sidePanelTab.Constructor = SidePanelTab
// NO CONFLICT
// =================
$.fn.sidePanelTab.noConflict = function () {
$.fn.sidePanelTab = old
return this
}
// DATA-API
// ============
$(window).load(function(){
$('[data-control=layout-sidepanel]').sidePanelTab()
})
// STORED PREFERENCES
// ====================
$(document).ready(function(){
if (Modernizr.touch || (typeof(localStorage) !== 'undefined' && localStorage.ocSidePanelFixed == 1)) {
$(document.body).removeClass('side-panel-not-fixed')
$(window).trigger('resize')
}
})
}(window.jQuery);

View File

@ -0,0 +1,72 @@
/*
* SimpleList control.
*
* Data attributes:
* - data-control="simplelist" - enables the simplelist plugin
*
* JavaScript API:
* $('#simplelist').simplelist()
*
* Dependences:
* - Sortable (jquery-sortable.js)
*/
+function ($) { "use strict";
var SimpleList = function (element, options) {
var $el = this.$el = $(element)
this.options = options || {}
if ($el.hasClass('is-sortable')) {
/*
* Make each list inside sortable
*/
var sortableOptions = {
distance: 10
}
if (this.options.sortableHandle)
sortableOptions[handle] = this.options.sortableHandle
$el.find('> ul, > ol').sortable(sortableOptions)
}
}
SimpleList.DEFAULTS = {
sortableHandle: null
}
// SIMPLE LIST PLUGIN DEFINITION
// ============================
var old = $.fn.simplelist
$.fn.simplelist = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.simplelist')
var options = $.extend({}, SimpleList.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.simplelist', (data = new SimpleList(this, options)))
})
}
$.fn.simplelist.Constructor = SimpleList
// SIMPLE LIST NO CONFLICT
// =================
$.fn.simplelist.noConflict = function () {
$.fn.simplelist = old
return this
}
// SIMPLE LIST DATA-API
// ===============
$(document).render(function(){
$('[data-control="simplelist"]').simplelist()
})
}(window.jQuery);

View File

@ -0,0 +1,665 @@
/*
* Sortable plugin
*
* Forked from: https://github.com/johnny/jquery-sortable/tree/271cd2c742439842000e6f92d4dae96bb8dcd595
*/
+function ($) { "use strict";
var eventNames,
isTouch = 'ontouchstart' in window,
cursorAdjustment,
containerDefaults = {
drag: true, // Items can be dragged from this container
drop: true, // Items can be droped onto this container
exclude: "", // Exclude items from being draggable, if the selector matches the item
nested: true, // Search for nested containers within an item
vertical: true // The items are assumed to be arranged vertically
},
groupDefaults = {
afterMove: function ($placeholder, container, $closestEl) {},
// The exact css path between the container and its items, e.g. "> tbody"
containerPath: "",
// The css selector of the containers
containerSelector: "ol, ul",
// Distance the mouse has to travel to start dragging
distance: 0,
// Time in milliseconds after mousedown until dragging should start.
// This option can be used to prevent unwanted drags when clicking on an element.
delay: 0,
// The css selector of the drag handle
handle: "",
// The exact css path between the item and its subcontainers
itemPath: "",
// The css selector of the items
itemSelector: "li",
// Check if the dragged item may be inside the container.
// Use with care, since the search for a valid container entails a depth first search
// and may be quite expensive.
isValidTarget: function ($item, container) {
return true
},
// Executed before onDrop if placeholder is detached.
// This happens if pullPlaceholder is set to false and the drop occurs outside a container.
onCancel: function ($item, container, _super, event) {
},
// Called after the drag has been started,
// that is the mouse button is beeing held down and
// the mouse is moving.
// The container is the closest initialized container.
// Therefore it might not be the container, that actually contains the item.
onDragStart: function ($item, container, _super, event) {
// Relative cursors position
var offset = $item.offset(),
pointer = container.rootGroup.pointer
cursorAdjustment = {
left: pointer.left - offset.left,
top: pointer.top - offset.top
}
$item.css({
height: $item.height(),
width: $item.width()
})
$item.addClass("dragged")
$("body").addClass("dragging")
},
// Executed at the beginning of a mouse move event.
// The Placeholder has not been moved yet.
onDrag: function ($item, position, _super, event) {
// Relative cursors position
$item.css({
left: position.left - cursorAdjustment.left,
top: position.top - cursorAdjustment.top
})
// Default behavior
// $item.css(position)
},
// Called when the mouse button is beeing released
onDrop: function ($item, container, _super, event) {
$item.removeClass("dragged").removeAttr("style")
$("body").removeClass("dragging")
},
// Called on mousedown. If falsy value is returned, the dragging will not start.
onMousedown: function ($item, _super, event) {
if (event.target.nodeName != 'INPUT' && event.target.nodeName != 'SELECT') {
event.preventDefault()
return true
}
},
// Template for the placeholder. Can be any valid jQuery input
// e.g. a string, a DOM element.
// The placeholder must have the class "placeholder"
placeholder: '<li class="placeholder"/>',
// If true, the position of the placeholder is calculated on every mousemove.
// If false, it is only calculated when the mouse is above a container.
pullPlaceholder: true,
// Specifies serialization of the container group.
// The pair $parent/$children is either container/items or item/subcontainers.
// Note that this default method only works, if every item only has one subcontainer
serialize: function ($parent, $children, parentIsContainer) {
var result = $.extend({}, $parent.data())
if (parentIsContainer)
return $children
else if ($children[0]) {
result.children = $children
delete result.subContainer
}
delete result.sortable
return result
},
// Set tolerance while dragging. Positive values decrease sensitivity,
// negative values increase it.
tolerance: 0
},
containerGroups = {},
groupCounter = 0,
emptyBox = {
left: 0,
top: 0,
bottom: 0,
right: 0
}
if (isTouch) {
eventNames = {
start: "touchstart",
drop: "touchend",
drag: "touchmove",
scroll: "scroll.sortable"
}
} else {
eventNames = {
start: "mousedown.sortable",
drop: "mouseup.sortable",
drag: "mousemove.sortable",
scroll: "scroll.sortable"
}
}
/*
* a is Array [left, right, top, bottom]
* b is array [left, top]
*/
function d(a, b) {
var x = Math.max(0, a[0] - b[0], b[0] - a[1]),
y = Math.max(0, a[2] - b[1], b[1] - a[3])
return x + y;
}
function setDimensions(array, dimensions, tolerance, useOffset) {
var i = array.length,
offsetMethod = useOffset ? "offset" : "position"
tolerance = tolerance || 0
while (i--) {
var el = array[i].el ? array[i].el : $(array[i]),
pos = el[offsetMethod]() // use fitting method
pos.left += parseInt(el.css('margin-left'), 10)
pos.top += parseInt(el.css('margin-top'), 10)
dimensions[i] = [
pos.left - tolerance,
pos.left + el.outerWidth() + tolerance,
pos.top - tolerance,
pos.top + el.outerHeight() + tolerance
]
}
}
function getRelativePosition(pointer, element) {
var offset = element.offset()
return {
left: pointer.left - offset.left,
top: pointer.top - offset.top
}
}
function sortByDistanceDesc(dimensions, pointer, lastPointer) {
pointer = [pointer.left, pointer.top]
lastPointer = lastPointer && [lastPointer.left, lastPointer.top]
var dim,
i = dimensions.length,
distances = []
while (i--) {
dim = dimensions[i]
distances[i] = [i, d(dim, pointer), lastPointer && d(dim, lastPointer)]
}
distances = distances.sort(function (a, b) {
return b[1] - a[1] || b[2] - a[2] || b[0] - a[0]
})
return distances // last entry is the closest
}
function ContainerGroup(options) {
this.options = $.extend({}, groupDefaults, options)
this.containers = []
if (!this.options.parentContainer) {
this.scrollProxy = $.proxy(this.scroll, this)
this.dragProxy = $.proxy(this.drag, this)
this.dropProxy = $.proxy(this.drop, this)
this.placeholder = $(this.options.placeholder)
if (!options.isValidTarget)
this.options.isValidTarget = undefined
}
}
ContainerGroup.get = function (options) {
if (!containerGroups[options.group]) {
if (!options.group)
options.group = groupCounter++
containerGroups[options.group] = new ContainerGroup(options)
}
return containerGroups[options.group]
}
ContainerGroup.prototype = {
dragInit: function (e, itemContainer) {
this.$document = $(itemContainer.el[0].ownerDocument)
if (itemContainer.enabled()) {
// get item to drag
this.item = $(e.target).closest(this.options.itemSelector)
this.itemContainer = itemContainer
if (this.item.is(this.options.exclude) ||
!this.options.onMousedown(this.item, groupDefaults.onMousedown, e)){
return
}
this.setPointer(e)
this.toggleListeners('on')
} else {
this.toggleListeners('on', ['drop'])
}
this.setupDelayTimer()
this.dragInitDone = true
},
drag: function (e) {
if (!this.dragging) {
if (!this.distanceMet(e) || !this.delayMet) {
return
}
this.options.onDragStart(this.item, this.itemContainer, groupDefaults.onDragStart, e)
this.item.before(this.placeholder)
this.dragging = true
}
this.setPointer(e)
// Place item under the cursor
this.options.onDrag(this.item,
getRelativePosition(this.pointer, this.item.offsetParent()),
groupDefaults.onDrag,
e)
var x = (isTouch) ? e.originalEvent.touches[0].pageX : e.pageX,
y = (isTouch) ? e.originalEvent.touches[0].pageY : e.pageY,
box = this.sameResultBox,
t = this.options.tolerance
if (!box || box.top - t > y || box.bottom + t < y || box.left - t > x || box.right + t < x) {
if (!this.searchValidTarget()) this.placeholder.detach()
}
},
drop: function (e) {
this.toggleListeners('off')
this.dragInitDone = false
if (this.dragging) {
// processing Drop, check if placeholder is detached
if (this.placeholder.closest("html")[0])
this.placeholder.before(this.item).detach()
else
this.options.onCancel(this.item, this.itemContainer, groupDefaults.onCancel, e)
this.options.onDrop(this.item, this.getContainer(this.item), groupDefaults.onDrop, e)
// cleanup
this.clearDimensions()
this.clearOffsetParent()
this.lastAppendedItem = this.sameResultBox = undefined
this.dragging = false
}
},
searchValidTarget: function (pointer, lastPointer) {
if (!pointer) {
pointer = this.relativePointer || this.pointer
lastPointer = this.lastRelativePointer || this.lastPointer
}
var distances = sortByDistanceDesc(this.getContainerDimensions(), pointer, lastPointer),
i = distances.length
while (i--) {
var index = distances[i][0],
distance = distances[i][1]
if (!distance || this.options.pullPlaceholder) {
var container = this.containers[index]
if (!container.disabled) {
if (!this.$getOffsetParent()) {
var offsetParent = container.getItemOffsetParent()
pointer = getRelativePosition(pointer, offsetParent)
lastPointer = getRelativePosition(lastPointer, offsetParent)
}
if (container.searchValidTarget(pointer, lastPointer))
return true
}
}
}
if (this.sameResultBox)
this.sameResultBox = undefined
},
movePlaceholder: function (container, item, method, sameResultBox) {
var lastAppendedItem = this.lastAppendedItem
if (!sameResultBox && lastAppendedItem && lastAppendedItem[0] === item[0])
return;
item[method](this.placeholder)
this.lastAppendedItem = item
this.sameResultBox = sameResultBox
this.options.afterMove(this.placeholder, container, item)
},
getContainerDimensions: function () {
if (!this.containerDimensions)
setDimensions(this.containers, this.containerDimensions = [], this.options.tolerance, !this.$getOffsetParent())
return this.containerDimensions
},
getContainer: function (element) {
return element.closest(this.options.containerSelector).data('oc.sortable')
},
$getOffsetParent: function () {
if (this.offsetParent === undefined) {
var i = this.containers.length - 1,
offsetParent = this.containers[i].getItemOffsetParent()
if (!this.options.parentContainer) {
while (i--) {
if (offsetParent[0] != this.containers[i].getItemOffsetParent()[0]) {
// If every container has the same offset parent,
// use position() which is relative to this parent,
// otherwise use offset()
// compare #setDimensions
offsetParent = false
break;
}
}
}
this.offsetParent = offsetParent
}
return this.offsetParent
},
setPointer: function (e) {
if (isTouch) {
var pointer = {
left: e.originalEvent.touches[0].pageX,
top: e.originalEvent.touches[0].pageY
}
} else {
var pointer = {
left: e.pageX,
top: e.pageY
}
}
if (this.$getOffsetParent()) {
var relativePointer = getRelativePosition(pointer, this.$getOffsetParent())
this.lastRelativePointer = this.relativePointer
this.relativePointer = relativePointer
}
this.lastPointer = this.pointer
this.pointer = pointer
},
distanceMet: function (e) {
if (isTouch) {
return (Math.max(
Math.abs(this.pointer.left - e.originalEvent.touches[0].pageX),
Math.abs(this.pointer.top - e.originalEvent.touches[0].pageY)
) >= this.options.distance)
} else {
return (Math.max(
Math.abs(this.pointer.left - e.pageX),
Math.abs(this.pointer.top - e.pageY)
) >= this.options.distance)
}
},
setupDelayTimer: function () {
var self = this
this.delayMet = !this.options.delay
if (!this.delayMet) {
clearTimeout(this._mouseDelayTimer);
this._mouseDelayTimer = setTimeout(function() {
self.delayMet = true
}, this.options.delay)
}
},
scroll: function (e) {
this.clearDimensions()
this.clearOffsetParent()
},
toggleListeners: function (method, events) {
var self = this
events = events || ['drag', 'drop', 'scroll']
$.each(events, function (i, event) {
self.$document[method](eventNames[event], self[event + 'Proxy'])
})
},
clearOffsetParent: function () {
this.offsetParent = undefined
},
// Recursively clear container and item dimensions
clearDimensions: function () {
this.containerDimensions = undefined
var i = this.containers.length
while (i--) {
this.containers[i].clearDimensions()
}
},
destroy: function () {
// TODO iterate over subgroups and destroy them
// TODO remove all events
containerGroups[this.options.group] = undefined
}
}
/*************************************************************************/
function Container(element, options) {
this.el = element
this.options = $.extend({}, containerDefaults, options)
this.group = ContainerGroup.get(this.options)
this.rootGroup = this.options.rootGroup || this.group
this.parentContainer = this.options.parentContainer
this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
var itemPath = this.rootGroup.options.itemPath,
target = itemPath ? this.el.find(itemPath) : this.el
//Start-Event binden
target.on(eventNames.start, this.handle, $.proxy(this.dragInit, this))
if (this.options.drop) {
this.group.containers.push(this)
}
}
Container.prototype = {
dragInit: function (e) {
var rootGroup = this.rootGroup
if (!rootGroup.dragInitDone && this.options.drag) {
rootGroup.dragInit(e, this)
}
},
searchValidTarget: function (pointer, lastPointer) {
var distances = sortByDistanceDesc(this.getItemDimensions(), pointer, lastPointer),
i = distances.length,
rootGroup = this.rootGroup,
validTarget = !rootGroup.options.isValidTarget || rootGroup.options.isValidTarget(rootGroup.item, this)
if (!i && validTarget) {
var itemPath = this.rootGroup.options.itemPath,
target = itemPath ? this.el.find(itemPath) : this.el
rootGroup.movePlaceholder(this, target, "append")
return true
} else {
while (i--) {
var index = distances[i][0],
distance = distances[i][1]
if (!distance && this.hasChildGroup(index)) {
var found = this.getContainerGroup(index).searchValidTarget(pointer, lastPointer)
if (found)
return true
}
else if (validTarget) {
this.movePlaceholder(index, pointer)
return true
}
}
}
},
movePlaceholder: function (index, pointer) {
var item = $(this.items[index]),
dim = this.itemDimensions[index],
method = "after",
width = item.outerWidth(),
height = item.outerHeight(),
offset = item.offset(),
sameResultBox = {
left: offset.left,
right: offset.left + width,
top: offset.top,
bottom: offset.top + height
}
if (this.options.vertical) {
var yCenter = (dim[2] + dim[3]) / 2,
inUpperHalf = pointer.top <= yCenter
if (inUpperHalf) {
method = "before"
sameResultBox.bottom -= height / 2
} else {
sameResultBox.top += height / 2
}
} else {
var xCenter = (dim[0] + dim[1]) / 2,
inLeftHalf = pointer.left <= xCenter
if (inLeftHalf) {
method = "before"
sameResultBox.right -= width / 2
} else {
sameResultBox.left += width / 2
}
}
if (this.hasChildGroup(index)) {
sameResultBox = emptyBox
}
this.rootGroup.movePlaceholder(this, item, method, sameResultBox)
},
getItemDimensions: function () {
if (!this.itemDimensions) {
this.items = this.$getChildren(this.el, "item").filter(":not(.placeholder, .dragged)").get()
setDimensions(this.items, this.itemDimensions = [], this.options.tolerance)
}
return this.itemDimensions
},
getItemOffsetParent: function () {
var offsetParent,
el = this.el
// Since el might be empty we have to check el itself and
// can not do something like el.children().first().offsetParent()
if (el.css("position") === "relative" || el.css("position") === "absolute" || el.css("position") === "fixed")
offsetParent = el
else
offsetParent = el.offsetParent()
return offsetParent
},
hasChildGroup: function (index) {
return this.options.nested && this.getContainerGroup(index)
},
getContainerGroup: function (index) {
var childGroup = $.data(this.items[index], "subContainer")
if (childGroup === undefined) {
var childContainers = this.$getChildren(this.items[index], "container")
childGroup = false
if (childContainers[0]) {
var options = $.extend({}, this.options, {
parentContainer: this,
rootGroup: this.rootGroup,
group: groupCounter++
})
childGroup = childContainers.sortable(options).data('oc.sortable').group
}
$.data(this.items[index], "subContainer", childGroup)
}
return childGroup
},
enabled: function () {
return !this.disabled && (!this.parentContainer || this.parentContainer.enabled())
},
$getChildren: function (parent, type) {
var options = this.rootGroup.options,
path = options[type + "Path"],
selector = options[type + "Selector"]
parent = $(parent)
if (path)
parent = parent.find(path)
return parent.children(selector)
},
_serialize: function (parent, isContainer) {
var self = this,
childType = isContainer ? "item" : "container",
children = this.$getChildren(parent, childType).not(this.options.exclude).map(function () {
return self._serialize($(this), !isContainer)
}).get()
return this.rootGroup.options.serialize(parent, children, isContainer)
},
clearDimensions: function () {
this.itemDimensions = undefined
if (this.items && this.items[0]) {
var i = this.items.length
while (i--) {
var group = $.data(this.items[i], "subContainer")
if (group)
group.clearDimensions()
}
}
}
}
var API = {
enable: function (ignoreChildren) {
this.disabled = false
},
disable: function (ignoreChildren) {
this.disabled = true
},
serialize: function () {
return this._serialize(this.el, true)
},
destroy: function () {
this.rootGroup.destroy()
}
}
$.extend(Container.prototype, API)
// SORTABLE PLUGIN DEFINITION
// ============================
var old = $.fn.sortable
$.fn.sortable = function (option) {
var args = Array.prototype.slice.call(arguments, 1)
return this.map(function () {
var $this = $(this),
object = $this.data('oc.sortable')
if (object && API[option])
return API[option].apply(object, args) || this
else if (!object && (option === undefined ||typeof option === "object"))
$this.data('oc.sortable', new Container($this, option))
return this
});
};
// SORTABLE NO CONFLICT
// =================
$.fn.sortable.noConflict = function () {
$.fn.sortable = old
return this
}
}(window.jQuery);

View File

@ -0,0 +1,74 @@
/*
* The stripe loading indicator.
*
* Displays the animated loading indicator stripe at the top of the page.
*
* JavaScript API:
* $.oc.stripeLoadIndicator.show(event)
* $.oc.stripeLoadIndicator.hide()
*
* By default if the show() method has been called several times, the hide() method should be
* called the same number of times in order to hide the stripe. Use hide(true) to hide the
* indicator forcibly.
*/
+function ($) { "use strict";
if ($.oc === undefined)
$.oc = {}
var StripeLoadIndicator = function () {
this.counter = 0
this.indicator = $('<div/>').addClass('stripe-loading-indicator loaded')
.append($('<div />').addClass('stripe'))
.append($('<div />').addClass('stripe-loaded'))
this.stripe = this.indicator.find('.stripe')
$(document.body).append(this.indicator)
}
StripeLoadIndicator.prototype.show = function() {
this.counter++
// Restart the animation
this.stripe.after(this.stripe = this.stripe.clone()).remove()
if (this.counter > 1)
return
this.indicator.removeClass('loaded')
$(document.body).addClass('loading')
}
StripeLoadIndicator.prototype.hide = function(force) {
this.counter--
if (force !== undefined && force)
this.counter = 0
if (this.counter <= 0) {
this.indicator.addClass('loaded')
$(document.body).removeClass('loading')
}
}
$(document).ready(function(){
$.oc.stripeLoadIndicator = new StripeLoadIndicator()
})
// STRIPE LOAD INDICATOR DATA-API
// ==============
$(document)
.on('ajaxPromise', '[data-stripe-load-indicator]', function() {
$.oc.stripeLoadIndicator.show()
// This code will cover instances where the element has been removed
// from the DOM, making the resolution event below an orphan.
var $el = $(this);
$(window).one('ajaxUpdateComplete', function(){
if ($el.closest('html').length === 0)
$.oc.stripeLoadIndicator.hide()
})
}).on('ajaxFail ajaxDone', '[data-stripe-load-indicator]', function() {
$.oc.stripeLoadIndicator.hide()
})
}(window.jQuery);

View File

@ -0,0 +1,435 @@
/*
* Tab control.
*
* This plugin is a wrapper for the Twitter Bootstrap Tab component. It provides the following features:
* - Adding tabs
* - Optional close icons with 2 states (modified / unmodified). The icon state can be changed by
* triggering the modified.oc.tab/unmodified.oc.tab events on any element within tab, or on the tab itself.
* - Removing tabs with the Close icon, or with triggering an event from inside a tab pane or tab.
* The removing can be canceled if the confirm.oc.tab event handler returns false.
* - Scrolling tabs if they do not fit the screen
* - Collapsible tabs
*
* Data attributes:
* - data-control="tab" - creates the tab control from an element
* - data-closable - enables the Close Tab feature
* - data-pane-classes - a list of CSS classes to apply new pane elements
*
* Example with data attributes (data-control="tab"):
*
* <div class="control-tabs master" data-control="tab" data-closable>
* <ul class="nav nav-tabs">
* <li class="active"><a href="#home">Home</a></li>
* </ul>
* <div class="tab-content">
* <div class="tab-pane active">Home</div>
* </div>
* </div>
*
* JavaScript methods:
* - $('#mytabs').ocTab({closable: true, closeConfirmation: 'Do you really want to close this tab? Unsaved data will be lost.'})
* - $('#mytabs').ocTab('addTab', 'Tab title', 'Tab content', identifier) - adds tab. The optional identifier parameter allows to
associate a identifier with a tab. The identifier can be used with the goTo() method to find and open a tab by it's identifier.
* - $('#mytabs').ocTab('closeTab', '.nav-tabs > li.active', true) - closes a tab. The second argument can point to a tab or tab pane.
The thrid argument determines whether the tab should be closed without the user confirmation. The default value is false.
* - $('.nav-tabs > li.active').trigger('close.oc.tab') - another way to close a tab. The event can be triggered on a tab, tab pane
* or any element inside a tab or tab pane.
* - $('#mytabs').ocTab('modifyTab', '.nav-tabs > li.active') - marks a tab as modified. Use the 'unmodifyTab' to mark a tab as unmodified.
* - $('.nav-tabs > li.active').trigger('modified.oc.tab') - another way to mark a tab as modified. The event can be triggered on a tab, tab pane
* or any element inside a tab or tab pane. Use the 'unmodified.oc.tab' to mark a tab as unmodified.
* - $('#mytabs').ocTab('goTo', 'someidentifier') - Finds a tab by it's identifier and opens it.
* - $('#mytabs').ocTab('goToPane', '.tab-content .tab-pane:first') - Opens a tab in context of it's content (pane element)
*
* Supported options:
* - closable - adds the "close" icon to the tab and lets users to close tabs. Corresponds the data-closable attribute.
* - closeConfirmation - a confirmation to show when a user tries to close a modified tab. Corresponds the data-close-confirmation
* attribute. The confirmation is displayed only if the tab was modified.
* - slidable - allows the tabs to be switched with the swipe gesture on touch devices. Corresponds the data-slidable attribute.
* - paneClasses - a list of CSS classes to apply new pane elements. Corresponds to the data-pane-classes attribute.
* - maxTitleSymbols - the maximum number of characters in tab titles.
* - titleAsFileNames - treat tab titles as file names. In this mode only the file name part is displayed in the tab, and the directory part
* is hidden.
*
* Events:
* - beforeClose.oc.tab - triggered on a tab pane element before tab is closed by the user. Call the event's
* preventDefault() method to cancel the action.
* - afterAllClosed.oc.tab - triggered after all tabs have been closed
*
* Dependences:
* - DragScroll (october.dragscroll.js)
* - Toolbar (october.toolbar.js)
* - Touchwipe (jquery.touchwipe.min.js)
*/
+function ($) { "use strict";
var Tab = function (element, options) {
var $el = this.$el = $(element);
this.options = options || {}
this.$tabsContainer = $('.nav-tabs', $el)
this.$pagesContainer = $('.tab-content', $el)
this.tabId = 'tabs' + $el.parents().length + Math.round(Math.random()*1000);
if (this.options.closable !== undefined && this.options.closable !== false)
$el.attr('data-closable', '')
this.init()
}
Tab.prototype.init = function() {
var self = this;
this.options.slidable = this.options.slidable !== undefined && this.options.slidable !== false
$('> li', this.$tabsContainer).each(function(index){
self.initTab(this)
})
this.$el.on('close.oc.tab', function(ev, data){
ev.preventDefault()
var force = (data !== undefined && data.force !== undefined) ? data.force : false;
self.closeTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div'), force)
})
this.$el.on('toggleCollapse.oc.tab', function(ev, data){
ev.preventDefault()
$(ev.target).closest('div.tab-content > div').toggleClass('collapsed')
})
this.$el.on('modified.oc.tab', function(ev){
ev.preventDefault()
self.modifyTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div'))
})
this.$el.on('unmodified.oc.tab', function(ev){
ev.preventDefault()
self.unmodifyTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div'))
})
this.$tabsContainer.on('shown.bs.tab', 'li', function(){
// self.$tabsContainer.dragScroll('fixScrollClasses')
$(window).trigger('oc.updateUi')
})
if (this.options.slidable) {
this.$pagesContainer.touchwipe({
wipeRight: function(){self.prev();},
wipeLeft: function(){self.next();},
preventDefaultEvents: false,
min_move_x: 60
});
}
this.$tabsContainer.toolbar({
scrollClassContainer: this.$el
})
this.updateClasses()
}
Tab.prototype.initTab = function(li) {
var
$tabs = $('>li', this.$tabsContainer),
tabIndex = $tabs.index(li),
targetId = this.tabId + '-tab-' + tabIndex,
$a = $('a', li)
$a.attr('data-target', '#'+targetId).attr('data-toggle', 'tab')
if (!$a.attr('title'))
$a.attr('title', $a.text())
var pane = $('> .tab-pane', this.$pagesContainer).eq(tabIndex).attr('id', targetId)
$(li).append($('<span class="tab-close"><i class="icon-times"></i></span>').click(function(){
$(this).trigger('close.oc.tab')
return false
}))
this.$el.trigger('initTab.oc.tab', [{'pane': pane, 'tab': li}])
}
Tab.prototype.addTab = function(title, content, identifier, tabClass) {
var
processedTitle = this.generateTitleText(title, -1),
$link = $('<a/>').attr('href', 'javascript:;').text(processedTitle),
$li = $('<li/>'),
$pane = $('<div>').html(content).addClass('tab-pane');
$link.attr('title', title)
$li.append($link)
this.$tabsContainer.append($li)
this.$pagesContainer.append($pane)
if (tabClass !== undefined)
$link.addClass(tabClass)
if (identifier !== undefined)
$li.attr('data-tab-id', identifier)
if (this.options.paneClasses !== undefined)
$pane.addClass(this.options.paneClasses)
this.initTab($li)
$link.tab('show')
$(window).trigger('resize')
this.$tabsContainer.dragScroll('goToElement', $li)
var defaultFocus = $('[default-focus]', $pane)
if (defaultFocus.is(":visible"))
defaultFocus.focus()
this.updateClasses()
}
Tab.prototype.updateTab = function(tab, title, content) {
var tabIndex = this.findTabIndex(tab)
if (tabIndex == -1)
return
var
processedTitle = this.generateTitleText(title, -1),
$tab = $('> li', this.$tabsContainer).eq(tabIndex),
$pane = $('> div', this.$pagesContainer).eq(tabIndex),
$link = $('a', $tab)
$link.text(processedTitle).attr('title', title)
$pane.html(content)
this.initTab($tab)
this.updateClasses()
}
Tab.prototype.generateTitleText = function(title, tabIndex)
{
var newTitle = title
if (this.options.titleAsFileNames)
newTitle = title.replace(/^.*[\\\/]/, '')
if (this.options.maxTitleSymbols && newTitle.length > this.options.maxTitleSymbols)
newTitle = '...'+newTitle.substring(newTitle.length - this.options.maxTitleSymbols)
return newTitle
}
Tab.prototype.closeTab = function(tab, force) {
var tabIndex = this.findTabIndex(tab)
if (tabIndex == -1)
return
var
$tab = $('> li', this.$tabsContainer).eq(tabIndex),
$pane = $('> div', this.$pagesContainer).eq(tabIndex),
isActive = $tab.hasClass('active'),
isModified = $tab.attr('data-modified') !== undefined;
if (isModified && this.options.closeConfirmation !== undefined && force !== true) {
if (!confirm(this.options.closeConfirmation))
return
}
var e = $.Event('beforeClose.oc.tab', { relatedTarget: $pane })
this.$el.trigger(e)
if (e.isDefaultPrevented())
return
$pane.remove()
$tab.remove()
if (isActive)
$('> li > a', this.$tabsContainer).eq(tabIndex-1).tab('show')
if ($('> li > a', this.$tabsContainer).length == 0)
this.$el.trigger('afterAllClosed.oc.tab')
this.$el.trigger('closed.oc.tab')
$(window).trigger('resize')
this.updateClasses()
}
Tab.prototype.updateClasses = function() {
if (this.$tabsContainer.children().length > 0)
this.$el.addClass('has-tabs')
else
this.$el.removeClass('has-tabs')
}
Tab.prototype.modifyTab = function(tab) {
var tabIndex = this.findTabIndex(tab)
if (tabIndex == -1)
return
$('> li', this.$tabsContainer).eq(tabIndex).attr('data-modified', '')
$('> div', this.$pagesContainer).eq(tabIndex).attr('data-modified', '')
}
Tab.prototype.unmodifyTab = function(tab) {
var tabIndex = this.findTabIndex(tab)
if (tabIndex == -1)
return
$('> li', this.$tabsContainer).eq(tabIndex).removeAttr('data-modified')
$('> div', this.$pagesContainer).eq(tabIndex).removeAttr('data-modified')
}
Tab.prototype.findTabIndex = function(tab) {
var tabToFind = tab
if (tab === undefined)
tabToFind = $('li.active', this.$tabsContainer)
var tabParent = this.$pagesContainer
if ($(tabToFind).parent().hasClass('nav-tabs'))
tabParent = this.$tabsContainer
return tabParent.children().index($(tabToFind))
}
Tab.prototype.findTabFromPane = function(pane) {
var id = '#' + $(pane).attr('id'),
tab = $('[data-target="' + id + '"]', this.$tabsContainer)
return tab
}
Tab.prototype.goTo = function(identifier) {
var $tab = $('[data-tab-id="'+identifier+'" ]', this.$tabsContainer)
if ($tab.length == 0)
return false
var tabIndex = this.findTabIndex($tab)
if (tabIndex == -1)
return false
this.goToIndex(tabIndex)
this.$tabsContainer.dragScroll('goToElement', $tab)
return true
}
Tab.prototype.goToPane = function(pane) {
var $pane = $(pane),
$tab = this.findTabFromPane($pane)
if ($pane.length == 0)
return
$pane.removeClass('collapsed')
var tabIndex = this.findTabIndex($pane)
if (tabIndex == -1)
return false
this.goToIndex(tabIndex)
if ($tab.length > 0)
this.$tabsContainer.dragScroll('goToElement', $tab)
return true
}
Tab.prototype.goToElement = function(element) {
return this.goToPane(element.closest('.tab-pane'))
}
Tab.prototype.findByIdentifier = function(identifier) {
return $('[data-tab-id="'+identifier+'" ]', this.$tabsContainer);
}
Tab.prototype.updateIdentifier = function(tab, identifier) {
var index = this.findTabIndex(tab)
if (index == -1)
return
$('> li', this.$tabsContainer).eq(index).attr('data-tab-id', identifier)
}
Tab.prototype.updateTitle = function(tab, title) {
var index = this.findTabIndex(tab)
if (index == -1)
return
var processedTitle = this.generateTitleText(title, index),
$link = $('> li > a', this.$tabsContainer).eq(index)
$link.attr('title', title)
$link.text(processedTitle)
}
Tab.prototype.goToIndex = function(index) {
$('> li > a', this.$tabsContainer).eq(index).tab('show')
}
Tab.prototype.prev = function() {
var tabIndex = this.findTabIndex()
if (tabIndex <= 0)
return
this.goToIndex(tabIndex-1)
}
Tab.prototype.next = function() {
var tabIndex = this.findTabIndex()
if (tabIndex == -1)
return
this.goToIndex(tabIndex+1)
}
Tab.DEFAULTS = {
}
// TAB PLUGIN DEFINITION
// ============================
var old = $.fn.ocTab
$.fn.ocTab = function (option) {
var args = arguments;
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.tab')
var options = $.extend({}, Tab.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.tab', (data = new Tab(this, options)))
if (typeof option == 'string') {
var methodArgs = [];
for (var i=1; i<args.length; i++)
methodArgs.push(args[i])
data[option].apply(data, methodArgs)
}
})
}
$.fn.ocTab.Constructor = Tab
// TAB NO CONFLICT
// =================
$.fn.ocTab.noConflict = function () {
$.fn.ocTab = old
return this
}
// TAB DATA-API
// ============
$(document).render(function(){
$('[data-control=tab]').ocTab()
})
/*
* Detect invalid fields, focus the tab
*/
$(window).on('ajaxInvalidField', function(ev, element, name){
element.closest('[data-control=tab]').ocTab('goToElement', element)
element.focus()
})
}(window.jQuery);

View File

@ -0,0 +1,79 @@
/*
* Toolbar control.
*
* Makes toolbars drag/scrollable.
*
* Data attributes:
* - data-control="toolbar" - enables the toolbar plugin
*
* JavaScript API:
* $('#toolbar').toolbar()
*
* Dependences:
* - Drag Scroll (october.dragscroll.js)
*/
+function ($) { "use strict";
var Toolbar = function (element, options) {
var
$el = this.$el = $(element),
$toolbar = $el.closest('.control-toolbar')
this.options = options || {};
var scrollClassContainer = options.scrollClassContainer !== undefined ?
options.scrollClassContainer :
$el.parent()
$el.dragScroll({
scrollClassContainer: scrollClassContainer
})
$('.form-control.growable', $toolbar).on('focus', function(){
update()
})
$('.form-control.growable', $toolbar).on('blur', function(){
update()
})
function update() {
$(window).trigger('resize')
}
}
Toolbar.DEFAULTS = {}
// TOOLBAR PLUGIN DEFINITION
// ============================
var old = $.fn.toolbar
$.fn.toolbar = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.toolbar')
var options = $.extend({}, Toolbar.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.toolbar', (data = new Toolbar(this, options)))
})
}
$.fn.toolbar.Constructor = Toolbar
// TOOLBAR NO CONFLICT
// =================
$.fn.toolbar.noConflict = function () {
$.fn.toolbar = old
return this
}
// TOOLBAR DATA-API
// ===============
$(document).on('render', function(){
$('[data-control=toolbar]').toolbar()
})
}(window.jQuery);

View File

@ -0,0 +1,75 @@
/*
* TreeList Widget
*
* Supported options:
* - handle - class name to use as a handle
*
* Events:
* - move.oc.treelist - triggered when a node on the tree is moved.
*
* Dependences:
* - Sortable Plugin (october.sortable.js)
*/
+function ($) { "use strict";
var TreeListWidget = function (element, options) {
var $el = this.$el = $(element),
self = this;
this.options = options || {};
$el.find('> ol').sortable({
handle: options.handle,
onDrop: function($item, container, _super) {
self.$el.trigger('move.oc.treelist', { item: $item, container: container })
_super($item, container)
}
})
}
TreeListWidget.DEFAULTS = {
handle: null
}
// TREELIST WIDGET PLUGIN DEFINITION
// ============================
var old = $.fn.treeListWidget
$.fn.treeListWidget = function (option) {
var args = arguments,
result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.treelist')
var options = $.extend({}, TreeListWidget.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.treelist', (data = new TreeListWidget(this, options)))
if (typeof option == 'string') result = data[option].call($this)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.treeListWidget.Constructor = TreeListWidget
// TREELIST WIDGET NO CONFLICT
// =================
$.fn.treeListWidget.noConflict = function () {
$.fn.treeListWidget = old
return this
}
// TREELIST WIDGET DATA-API
// ==============
$(document).render(function(){
$('[data-control="treelist"]').treeListWidget();
})
}(window.jQuery);

View File

@ -0,0 +1,121 @@
/*
* The trigger API.
*
* The API allows to change elements' visibility or status (enabled/disabled) basing on
* other elements' statuses. Example: enable a button if any checkbox inside another
* element is checked.
*
* Supported data attributes:
* - data-trigger-type, values: display, hide, enable, disable
* - data-trigger: a CSS selector for elements that trigger the action (checkboxes)
* - data-trigger-condition, values: checked (more conditions to add later) - determines the condition the elements
* specified in the data-trigger should satisfy in order the condition to be considered as "true".
*
* Example: <input type="button" class="btn disabled"
* data-trigger-type="enable"
* data-trigger="#cblist input[type=checkbox]"
* data-trigger-condition="checked" ... >
*
* Supported events:
* - oc.triggerOn.update - triggers the update. Trigger this event on the element the plugin is bound to to
* force it to check the condition and update itself. This is useful when the page content is updated with AJAX.
*
* JavaScript API:
* $('#mybutton').triggerOn({triggerCondition: 'checked', trigger: '#cblist input[type=checkbox]', triggerType: 'enable'})
*/
+function ($) { "use strict";
var TriggerOn = function (element, options) {
var $el = this.$el = $(element);
this.options = options || {};
if (this.options.triggerCondition === false)
throw new Error('Trigger condition is not specified.')
if (this.options.trigger === false)
throw new Error('Trigger selector is not specified.')
if (this.options.triggerType === false)
throw new Error('Trigger type is not specified.')
if (this.options.triggerCondition == 'checked')
$(document).on('change', this.options.trigger, $.proxy(this.onConditionChanged, this))
var self = this;
$el.on('oc.triggerOn.update', function(e){
e.stopPropagation()
self.onConditionChanged()
})
self.onConditionChanged()
}
TriggerOn.prototype.onConditionChanged = function() {
if (this.options.triggerCondition == 'checked')
this.updateTarget($(this.options.trigger + ':checked').length > 0);
}
TriggerOn.prototype.updateTarget = function(status) {
if (this.options.triggerType == 'display')
this.$el.toggleClass('hide', !status).trigger('hide', [!status])
else if (this.options.triggerType == 'hide')
this.$el.toggleClass('hide', status).trigger('hide', [status])
else if (this.options.triggerType == 'enable')
this.$el.prop('disabled', !status).trigger('disable', [!status]).toggleClass('control-disabled', !status)
else if (this.options.triggerType == 'disable')
this.$el.prop('disabled', status).trigger('disable', [status]).toggleClass('control-disabled', status)
if (this.options.triggerType == 'display' || this.options.triggerType == 'hide')
this.fixButtonClasses()
$(window).trigger('resize')
}
TriggerOn.prototype.fixButtonClasses = function() {
var group = this.$el.closest('.btn-group')
if (group.length > 0 && this.$el.is(':last-child'))
this.$el.prev().toggleClass('last', this.$el.hasClass('hide'))
}
TriggerOn.DEFAULTS = {
triggerCondition: false,
trigger: false,
triggerType: false
}
// TRIGGERON PLUGIN DEFINITION
// ============================
var old = $.fn.triggerOn
$.fn.triggerOn = function (option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.triggerOn')
var options = $.extend({}, TriggerOn.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.triggerOn', (data = new TriggerOn(this, options)))
})
}
$.fn.triggerOn.Constructor = TriggerOn
// TRIGGERON NO CONFLICT
// =================
$.fn.triggerOn.noConflict = function () {
$.fn.triggerOn = old
return this
}
// TRIGGERON DATA-API
// ===============
$(document).ready(function(){
$('[data-trigger]').triggerOn()
})
}(window.jQuery);

View File

@ -0,0 +1,196 @@
/*
* October General Utilities
*/
/*
* Path helpers
*/
function backendUrl(url) {
if (typeof backendBasePath === 'undefined' || !backendBasePath)
return url;
if (url.substr(0,1) == '/')
url = url.substr(1);
return backendBasePath + url;
}
/*
* Lock Manager
*/
LockManager = function() {
var o = {
locks: {},
set: function(name) {
o.locks[name] = true;
},
get: function(name) {
return (o.locks[name]);
},
remove: function(name) {
o.locks[name] = null;
}
};
return o;
};
lockManager = new LockManager();
/*
* Asset Manager
*
* Usage: assetManager.load({ css:[], js:[], img:[] }, onLoadedCallback)
*/
AssetManager = function() {
var o = {
load: function(collection, callback) {
var jsList = (collection.js) ? collection.js : [],
cssList = (collection.css) ? collection.css : [],
imgList = (collection.img) ? collection.img : []
jsList = $.grep(jsList, function(item){
return $('head script[src="'+item+'"]').length == 0
})
cssList = $.grep(cssList, function(item){
return $('head link[href="'+item+'"]').length == 0
})
var cssCounter = 0,
jsLoaded = false,
imgLoaded = false
if (jsList.length === 0 && cssList.length === 0 && imgList.length === 0) {
callback && callback()
return
}
o.loadJavaScript(jsList, function(){
jsLoaded = true
checkLoaded()
})
$.each(cssList, function(index, source){
o.loadStyleSheet(source, function(){
cssCounter++
checkLoaded()
})
})
o.loadImage(imgList, function(){
imgLoaded = true
checkLoaded()
})
function checkLoaded() {
if (!imgLoaded)
return false
if (!jsLoaded)
return false
if (cssCounter < cssList.length)
return false
callback && callback()
}
},
/*
* Loads StyleSheet files
*/
loadStyleSheet: function(source, callback) {
var cssElement = document.createElement('link')
cssElement.setAttribute('rel', 'stylesheet')
cssElement.setAttribute('type', 'text/css')
cssElement.setAttribute('href', source)
cssElement.addEventListener('load', callback, false)
if (typeof cssElement != 'undefined') {
document.getElementsByTagName('head')[0].appendChild(cssElement)
}
return cssElement
},
/*
* Loads JavaScript files in sequence
*/
loadJavaScript: function(sources, callback) {
if (sources.length <= 0)
return callback()
var source = sources.shift(),
jsElement = document.createElement('script');
jsElement.setAttribute('type', 'text/javascript')
jsElement.setAttribute('src', source)
jsElement.addEventListener('load', function() {
o.loadJavaScript(sources, callback)
}, false)
if (typeof jsElement != 'undefined') {
document.getElementsByTagName('head')[0].appendChild(jsElement)
}
},
/*
* Loads Image files
*/
loadImage: function(sources, callback) {
if (sources.length <= 0)
return callback()
var loaded = 0
$.each(sources, function(index, source){
var img = new Image()
img.onload = function() {
if (++loaded == sources.length && callback)
callback()
}
img.src = source
})
}
};
return o;
};
assetManager = new AssetManager();
/*
* Inverse Click Event
*
* Calls the handler function if the user has clicked outside the object
* and not on any of the elements in the exception list.
*/
$.fn.extend({
clickOutside: function(handler, exceptions) {
var $this = this;
$('body').on('click', function(event) {
if (exceptions && $.inArray(event.target, exceptions) > -1) {
return;
} else if ($.contains($this[0], event.target)) {
return;
} else {
handler(event, $this);
}
});
return this;
}
})

View File

@ -0,0 +1,157 @@
/*
* Creates a vertical responsive menu.
*
* JavaScript API:
* $('#menu').verticalMenu()
*
* Dependences:
* - Drag Scroll (october.dragscroll.js)
*/
+function ($) { "use strict";
var VerticalMenu = function (element, toggle, options) {
this.body = $('body')
this.toggle = $(toggle)
this.options = options || {}
this.options = $.extend({}, VerticalMenu.DEFAULTS, this.options)
this.wrapper = $(this.options.contentWrapper)
/*
* Insert the menu
*/
this.menuPanel = $('<div></div>').appendTo('body').addClass(this.options.collapsedMenuClass).css('width', 0)
this.menuContainer = $('<div></div>').appendTo(this.menuPanel).css('display', 'none')
this.menuElement = $(element).clone().appendTo(this.menuContainer).css('width', 'auto')
var self = this
/*
* Handle the menu toggle click
*/
this.toggle.click(function() {
if (!self.body.hasClass(self.options.bodyMenuOpenClass)) {
var wrapperWidth = self.wrapper.outerWidth()
self.menuElement.dragScroll('goToStart')
self.wrapper.css({
'position': 'absolute',
'min-width': self.wrapper.width(),
'height': '100%'
})
self.body.addClass(self.options.bodyMenuOpenClass)
self.menuContainer.css('display', 'block')
self.wrapper.animate({'left': self.options.menuWidth}, { duration: 200, queue: false })
self.menuPanel.animate({'width': self.options.menuWidth},
{
duration: 200,
queue: false,
complete: function() {
self.menuElement.css('width', self.options.menuWidth)
}
})
} else {
closeMenu()
}
return false
})
this.wrapper.click(function() {
if (self.body.hasClass(self.options.bodyMenuOpenClass)) {
closeMenu()
return false
}
})
/*
* Disable the menu if the window is wider than the breakpoint width
*/
$(window).resize(function() {
if (self.body.hasClass(self.options.bodyMenuOpenClass)) {
if ($(window).width() > self.options.breakpoint) {
hideMenu()
}
}
})
/*
* Make the menu draggable
*/
this.menuElement.dragScroll({
vertical: true,
start: function(){self.menuElement.addClass('drag')},
stop: function(){self.menuElement.removeClass('drag')},
scrollClassContainer: self.menuPanel,
scrollMarkerContainer: self.menuContainer
})
this.menuElement.on('click', function() {
// Do not handle menu item clicks while dragging
if (self.menuElement.hasClass('drag'))
return false
})
/*
* Internal event, completely hides the menu
*/
function hideMenu() {
self.body.removeClass(self.options.bodyMenuOpenClass)
self.wrapper.css({
'position': 'static',
'min-width': 0,
'right': 0,
'height': '100%'
})
self.menuPanel.css('width', 0)
self.menuElement.css('width', 'auto')
self.menuContainer.css('display', 'none')
}
/*
* Internal event, smoothly collapses the menu
*/
function closeMenu() {
self.wrapper.animate({'left': 0}, { duration: 200, queue: false})
self.menuPanel.animate({'width': 0}, { duration: 200, queue: false, complete: hideMenu })
self.menuElement.animate({'width': 0}, { duration: 200, queue: false })
}
}
VerticalMenu.DEFAULTS = {
menuWidth: 250,
minContentWidth: 769,
breakpoint: 769,
bodyMenuOpenClass: 'mainmenu-open',
collapsedMenuClass: 'mainmenu-collapsed',
contentWrapper: '#layout-canvas'
}
// VERTICAL MENU PLUGIN DEFINITION
// ============================
var old = $.fn.verticalMenu
$.fn.verticalMenu = function (toggleSelector, option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('oc.verticalMenu')
var options = typeof option == 'object' && option
if (!data) $this.data('oc.verticalMenu', (data = new VerticalMenu(this, toggleSelector, options)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.verticalMenu.Constructor = VerticalMenu
// VERTICAL MENU NO CONFLICT
// =================
$.fn.verticalMenu.noConflict = function () {
$.fn.verticalMenu = old
return this
}
}(window.jQuery);

Some files were not shown because too many files have changed in this diff Show More