Welcome to the world, October :-)
|
|
@ -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',
|
||||
|
||||
)),
|
||||
|
||||
);
|
||||
|
|
@ -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',
|
||||
|
||||
);
|
||||
|
|
@ -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],
|
||||
|
||||
);
|
||||
|
|
@ -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,
|
||||
),
|
||||
|
||||
),
|
||||
|
||||
);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -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,
|
||||
|
||||
);
|
||||
|
|
@ -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',
|
||||
|
||||
),
|
||||
|
||||
);
|
||||
|
|
@ -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,
|
||||
|
||||
);
|
||||
|
|
@ -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,
|
||||
);
|
||||
|
|
@ -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',
|
||||
|
||||
);
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
|
@ -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.
|
||||
|
|
||||
*/
|
||||
|
|
@ -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.
|
||||
|
|
||||
*/
|
||||
|
||||
|
|
@ -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';
|
||||
|
|
@ -0,0 +1 @@
|
|||
services.manifest
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
|
|
@ -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();
|
||||
|
||||
|
||||
|
|
@ -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',
|
||||
|
||||
);
|
||||
|
|
@ -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;
|
||||
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
&quot;License&quot; shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
&quot;Licensor&quot; shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
&quot;Legal Entity&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, &quot;control&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.
|
||||
|
||||
&quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
&quot;Source&quot; form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
&quot;Object&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.
|
||||
|
||||
&quot;Work&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).
|
||||
|
||||
&quot;Derivative Works&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.
|
||||
|
||||
&quot;Contribution&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, &quot;submitted&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 &quot;Not a Contribution.&quot;
|
||||
|
||||
&quot;Contributor&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 &quot;NOTICE&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 &quot;AS IS&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.
|
||||
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 3.7 KiB |
|
|
@ -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 |
|
After Width: | Height: | Size: 7.9 KiB |
|
|
@ -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 |
|
After Width: | Height: | Size: 224 B |
|
|
@ -0,0 +1,5 @@
|
|||
$(document).ready(function(){
|
||||
$(document.body).removeClass('preload')
|
||||
|
||||
$('form input[type=text], form input[type=password]').first().focus()
|
||||
})
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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)
|
||||
*/
|
||||
|
||||
|
|
@ -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">×</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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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">×</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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
})
|
||||
|
|
@ -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);
|
||||