diff --git a/composer.json b/composer.json index 108ea8473..20cbe46a7 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,6 @@ "bower-asset/base64": "^1.0", "bower-asset/chart-js": "^2.8", "bower-asset/dompurify": "^1.0", - "bower-asset/perfect-scrollbar": "^0.6", "bower-asset/vue": "^2.6", "npm-asset/es-jquery-sortable": "^0.9.13", "npm-asset/jquery": "^2.0", diff --git a/composer.lock b/composer.lock index 8834f3f70..c04c2e9b1 100644 --- a/composer.lock +++ b/composer.lock @@ -237,37 +237,6 @@ ], "time": "2019-02-28T15:21:34+00:00" }, - { - "name": "bower-asset/perfect-scrollbar", - "version": "0.6.16", - "source": { - "type": "git", - "url": "https://github.com/utatti/perfect-scrollbar-bower.git", - "reference": "3049129e5dbb403295ce8507a461cdd0f200938c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utatti/perfect-scrollbar-bower/zipball/3049129e5dbb403295ce8507a461cdd0f200938c", - "reference": "3049129e5dbb403295ce8507a461cdd0f200938c", - "shasum": "" - }, - "type": "bower-asset-library", - "extra": { - "bower-asset-main": [ - "css/perfect-scrollbar.css", - "js/perfect-scrollbar.js" - ], - "bower-asset-ignore": [ - "**/.*", - "bower_components" - ] - }, - "license": [ - "MIT" - ], - "description": "Minimalistic but perfect custom scrollbar plugin", - "time": "2017-01-10T01:04:09+00:00" - }, { "name": "bower-asset/vue", "version": "v2.6.10", diff --git a/database.sql b/database.sql index 3ae2374fd..5f6fc2932 100644 --- a/database.sql +++ b/database.sql @@ -715,24 +715,6 @@ CREATE TABLE IF NOT EXISTS `item-content` ( INDEX `uri-id` (`uri-id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Content for all posts'; --- --- TABLE item-delivery-data --- -CREATE TABLE IF NOT EXISTS `item-delivery-data` ( - `iid` int unsigned NOT NULL COMMENT 'Item id', - `postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery', - `inform` mediumtext COMMENT 'Additional receivers of the linked item', - `queue_count` mediumint NOT NULL DEFAULT 0 COMMENT 'Initial number of delivery recipients, used as item.delivery_queue_count', - `queue_done` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries, used as item.delivery_queue_done', - `queue_failed` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of unsuccessful deliveries, used as item.delivery_queue_failed', - `activitypub` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via ActivityPub', - `dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via DFRN', - `legacy_dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via legacy DFRN', - `diaspora` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via Diaspora', - `ostatus` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via OStatus', - PRIMARY KEY(`iid`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items'; - -- -- TABLE item-uri -- @@ -1200,6 +1182,24 @@ CREATE TABLE IF NOT EXISTS `post-category` ( INDEX `uri-id` (`tid`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to categories'; +-- +-- TABLE post-delivery-data +-- +CREATE TABLE IF NOT EXISTS `post-delivery-data` ( + `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', + `postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery', + `inform` mediumtext COMMENT 'Additional receivers of the linked item', + `queue_count` mediumint NOT NULL DEFAULT 0 COMMENT 'Initial number of delivery recipients, used as item.delivery_queue_count', + `queue_done` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries, used as item.delivery_queue_done', + `queue_failed` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of unsuccessful deliveries, used as item.delivery_queue_failed', + `activitypub` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via ActivityPub', + `dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via DFRN', + `legacy_dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via legacy DFRN', + `diaspora` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via Diaspora', + `ostatus` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via OStatus', + PRIMARY KEY(`uri-id`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items'; + -- -- TABLE post-tag -- @@ -1569,22 +1569,6 @@ CREATE VIEW `owner-view` AS SELECT INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self` INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid`; --- --- VIEW participation-view --- -DROP VIEW IF EXISTS `participation-view`; -CREATE VIEW `participation-view` AS SELECT - `participation`.`iid` AS `iid`, - `contact`.`id` AS `id`, - `contact`.`url` AS `url`, - `contact`.`name` AS `name`, - `contact`.`protocol` AS `protocol`, - CASE `contact`.`batch` WHEN '' THEN `fcontact`.`batch` ELSE `contact`.`batch` END AS `batch`, - CASE `fcontact`.`network` WHEN '' THEN `contact`.`network` ELSE `fcontact`.`network` END AS `network` - FROM `participation` - INNER JOIN `contact` ON `contact`.`id` = `participation`.`cid` AND NOT `contact`.`archive` - INNER JOIN `fcontact` ON `fcontact`.`id` = `participation`.`fid`; - -- -- VIEW pending-view -- diff --git a/include/enotify.php b/include/enotify.php index 78a390e65..ae2e2e7fe 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -107,12 +107,24 @@ function notification($params) $item_id = 0; } + if (isset($params['item']['uri-id'])) { + $uri_id = $params['item']['uri-id']; + } else { + $uri_id = 0; + } + if (isset($params['parent'])) { $parent_id = $params['parent']; } else { $parent_id = 0; } + if (isset($params['item']['parent-uri-id'])) { + $parent_uri_id = $params['item']['parent-uri-id']; + } else { + $parent_uri_id = 0; + } + $epreamble = ''; $preamble = ''; $subject = ''; @@ -452,17 +464,19 @@ function notification($params) if ($show_in_notification_page) { $notification = DI::notify()->insert([ - 'name' => $params['source_name'] ?? '', - 'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'] ?? '')), 0, 255), - 'url' => $params['source_link'] ?? '', - 'photo' => $params['source_photo'] ?? '', - 'link' => $itemlink ?? '', - 'uid' => $params['uid'] ?? 0, - 'iid' => $item_id ?? 0, - 'parent' => $parent_id ?? 0, - 'type' => $params['type'] ?? '', - 'verb' => $params['verb'] ?? '', - 'otype' => $params['otype'] ?? '', + 'name' => $params['source_name'] ?? '', + 'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'] ?? '')), 0, 255), + 'url' => $params['source_link'] ?? '', + 'photo' => $params['source_photo'] ?? '', + 'link' => $itemlink ?? '', + 'uid' => $params['uid'] ?? 0, + 'iid' => $item_id, + 'uri-id' => $uri_id, + 'parent' => $parent_id, + 'parent-uri-id' => $parent_uri_id, + 'type' => $params['type'] ?? '', + 'verb' => $params['verb'] ?? '', + 'otype' => $params['otype'] ?? '', ]); $notification->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $notification->link]); @@ -486,8 +500,9 @@ function notification($params) if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) { Logger::log("notify_id:" . intval($notify_id) . ", parent: " . intval($params['parent']) . "uid: " . intval($params['uid']), Logger::DEBUG); - $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'], - 'receiver-uid' => $params['uid'], 'parent-item' => 0]; + $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'], + 'master-parent-uri-id' => $parent_uri_id, + 'receiver-uid' => $params['uid'], 'parent-item' => 0]; DBA::insert('notify-threads', $fields); $additional_mail_header .= "Message-ID: <${id_for_parent}>\n"; @@ -574,7 +589,7 @@ function check_user_notification($itemid) { * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ function check_item_notification($itemid, $uid, $notification_type) { - $fields = ['id', 'mention', 'parent', 'title', 'body', + $fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'title', 'body', 'author-link', 'author-name', 'author-avatar', 'author-id', 'guid', 'parent-uri', 'uri', 'contact-id', 'network']; $condition = ['id' => $itemid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'deleted' => false]; diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 9bce1f1f3..25cf99142 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -74,6 +74,9 @@ class PostUpdate if (!self::update1342()) { return false; } + if (!self::update1345()) { + return false; + } if (!self::update1346()) { return false; } @@ -564,7 +567,6 @@ class PostUpdate Logger::info('Start', ['item' => $id]); - $start_id = $id; $rows = 0; $items = DBA::p("SELECT `uri-id`,`body` FROM `item-content` WHERE @@ -618,7 +620,6 @@ class PostUpdate Logger::info('Start', ['item' => $id]); - $start_id = $id; $rows = 0; $terms = DBA::p("SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body` @@ -673,6 +674,60 @@ class PostUpdate return false; } + /** + * Fill the "post-delivery-data" table with data from the "item-delivery-data" table + * + * @return bool "true" when the job is done + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function update1345() + { + // Was the script completed? + if (DI::config()->get('system', 'post_update_version') >= 1345) { + return true; + } + + $id = DI::config()->get('system', 'post_update_version_1345_id', 0); + Logger::info('Start', ['item' => $id]); + + $rows = 0; + + $deliveries = DBA::p("SELECT `uri-id`, `iid`, `item-delivery-data`.`postopts`, `item-delivery-data`.`inform`, + `queue_count`, `queue_done`, `activitypub`, `dfrn`, `diaspora`, `ostatus`, `legacy_dfrn`, `queue_failed` + FROM `item-delivery-data` + INNER JOIN `item` ON `item`.`id` = `item-delivery-data`.`iid` + WHERE `iid` >= ? ORDER BY `iid` LIMIT 10000", $id); + + if (DBA::errorNo() != 0) { + Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); + return false; + } + + while ($delivery = DBA::fetch($deliveries)) { + $id = $delivery['iid']; + unset($delivery['iid']); + DBA::insert('post-delivery-data', $delivery); + ++$rows; + } + DBA::close($deliveries); + + DI::config()->set('system', 'post_update_version_1345_id', $id); + + Logger::info('Processed', ['rows' => $rows, 'last' => $id]); + + // When there are less than 100 items processed this means that we reached the end + // The other entries will then be processed with the regular functionality + if ($rows < 100) { + DI::config()->set('system', 'post_update_version', 1345); + Logger::info('Done'); + return true; + } + + return false; + } + + + /** * Fill the "tag" table with tags and mentions from the "term" table * @@ -733,5 +788,5 @@ class PostUpdate } return false; - } + } } diff --git a/src/Model/Item.php b/src/Model/Item.php index 8c66e05f4..17c841fc2 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -284,7 +284,7 @@ class Item // Fetch data from the item-content table whenever there is content there if (self::isLegacyMode()) { - $legacy_fields = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); + $legacy_fields = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); foreach ($legacy_fields as $field) { if (empty($row[$field]) && !empty($row['internal-item-' . $field])) { $row[$field] = $row['internal-item-' . $field]; @@ -685,7 +685,7 @@ class Item $fields['item-content'] = array_merge(self::CONTENT_FIELDLIST, self::MIXED_CONTENT_FIELDLIST); - $fields['item-delivery-data'] = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, ItemDeliveryData::FIELD_LIST); + $fields['post-delivery-data'] = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, Post\DeliveryData::FIELD_LIST); $fields['permissionset'] = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']; @@ -807,8 +807,8 @@ class Item $joins .= " LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`"; } - if (strpos($sql_commands, "`item-delivery-data`.") !== false) { - $joins .= " LEFT JOIN `item-delivery-data` ON `item-delivery-data`.`iid` = `item`.`id`"; + if (strpos($sql_commands, "`post-delivery-data`.") !== false) { + $joins .= " LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `item`.`uri-id` AND `item`.`origin`"; } if (strpos($sql_commands, "`permissionset`.") !== false) { @@ -848,7 +848,7 @@ class Item $selected[] = 'internal-user-ignored'; } - $legacy_fields = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); + $legacy_fields = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); $selection = []; foreach ($fields as $table => $table_fields) { @@ -934,7 +934,7 @@ class Item } } - $delivery_data = ItemDeliveryData::extractFields($fields); + $delivery_data = Post\DeliveryData::extractFields($fields); $clear_fields = ['bookmark', 'type', 'author-name', 'author-avatar', 'author-link', 'owner-name', 'owner-avatar', 'owner-link', 'postopts', 'inform']; foreach ($clear_fields as $field) { @@ -1022,7 +1022,7 @@ class Item } } - ItemDeliveryData::update($item['id'], $delivery_data); + Post\DeliveryData::update($item['uri-id'], $delivery_data); self::updateThread($item['id']); @@ -1182,7 +1182,7 @@ class Item self::markForDeletion(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority); } - ItemDeliveryData::delete($item['id']); + Post\DeliveryData::delete($item['uri-id']); // We don't delete the item-activity here, since we need some of the data for ActivityPub @@ -1838,7 +1838,7 @@ class Item self::insertContent($item); } - $delivery_data = ItemDeliveryData::extractFields($item); + $delivery_data = Post\DeliveryData::extractFields($item); unset($item['postopts']); unset($item['inform']); @@ -1942,7 +1942,7 @@ class Item } if (!empty($item['origin']) || !empty($item['wall']) || !empty($delivery_data['postopts']) || !empty($delivery_data['inform'])) { - ItemDeliveryData::insert($current_post, $delivery_data); + Post\DeliveryData::insert($item['uri-id'], $delivery_data); } DBA::commit(); diff --git a/src/Model/ItemDeliveryData.php b/src/Model/Post/DeliveryData.php similarity index 63% rename from src/Model/ItemDeliveryData.php rename to src/Model/Post/DeliveryData.php index a26c80b1c..0feb38281 100644 --- a/src/Model/ItemDeliveryData.php +++ b/src/Model/Post/DeliveryData.php @@ -19,12 +19,12 @@ * */ -namespace Friendica\Model; +namespace Friendica\Model\Post; use Friendica\Database\DBA; use \BadMethodCallException; -class ItemDeliveryData +class DeliveryData { const LEGACY_FIELD_LIST = [ // Legacy fields moved from item table @@ -55,7 +55,7 @@ class ItemDeliveryData public static function extractFields(array &$fields) { $delivery_data = []; - foreach (array_merge(ItemDeliveryData::FIELD_LIST, ItemDeliveryData::LEGACY_FIELD_LIST) as $key => $field) { + foreach (array_merge(self::FIELD_LIST, self::LEGACY_FIELD_LIST) as $key => $field) { if (is_int($key) && isset($fields[$field])) { // Legacy field moved from item table $delivery_data[$field] = $fields[$field]; @@ -71,16 +71,16 @@ class ItemDeliveryData } /** - * Increments the queue_done for the given item ID. + * Increments the queue_done for the given URI ID. * * Avoids racing condition between multiple delivery threads. * - * @param integer $item_id + * @param integer $uri_id * @param integer $protocol * @return bool * @throws \Exception */ - public static function incrementQueueDone($item_id, $protocol = 0) + public static function incrementQueueDone(int $uri_id, int $protocol = 0) { $sql = ''; @@ -102,69 +102,69 @@ class ItemDeliveryData break; } - return DBA::e('UPDATE `item-delivery-data` SET `queue_done` = `queue_done` + 1' . $sql . ' WHERE `iid` = ?', $item_id); + return DBA::e('UPDATE `post-delivery-data` SET `queue_done` = `queue_done` + 1' . $sql . ' WHERE `uri-id` = ?', $uri_id); } /** - * Increments the queue_failed for the given item ID. + * Increments the queue_failed for the given URI ID. * * Avoids racing condition between multiple delivery threads. * - * @param integer $item_id + * @param integer $uri_id * @return bool * @throws \Exception */ - public static function incrementQueueFailed($item_id) + public static function incrementQueueFailed(int $uri_id) { - return DBA::e('UPDATE `item-delivery-data` SET `queue_failed` = `queue_failed` + 1 WHERE `iid` = ?', $item_id); + return DBA::e('UPDATE `post-delivery-data` SET `queue_failed` = `queue_failed` + 1 WHERE `uri-id` = ?', $uri_id); } /** - * Increments the queue_count for the given item ID. + * Increments the queue_count for the given URI ID. * - * @param integer $item_id + * @param integer $uri_id * @param integer $increment * @return bool * @throws \Exception */ - public static function incrementQueueCount(int $item_id, int $increment = 1) + public static function incrementQueueCount(int $uri_id, int $increment = 1) { - return DBA::e('UPDATE `item-delivery-data` SET `queue_count` = `queue_count` + ? WHERE `iid` = ?', $increment, $item_id); + return DBA::e('UPDATE `post-delivery-data` SET `queue_count` = `queue_count` + ? WHERE `uri-id` = ?', $increment, $uri_id); } /** - * Insert a new item delivery data entry + * Insert a new URI delivery data entry * - * @param integer $item_id + * @param integer $uri_id * @param array $fields * @return bool * @throws \Exception */ - public static function insert($item_id, array $fields) + public static function insert(int $uri_id, array $fields) { - if (empty($item_id)) { - throw new BadMethodCallException('Empty item_id'); + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); } - $fields['iid'] = $item_id; + $fields['uri-id'] = $uri_id; - return DBA::insert('item-delivery-data', $fields); + return DBA::insert('post-delivery-data', $fields); } /** - * Update/Insert item delivery data + * Update/Insert URI delivery data * * If you want to update queue_done, please use incrementQueueDone instead. * - * @param integer $item_id + * @param integer $uri_id * @param array $fields * @return bool * @throws \Exception */ - public static function update($item_id, array $fields) + public static function update(int $uri_id, array $fields) { - if (empty($item_id)) { - throw new BadMethodCallException('Empty item_id'); + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); } if (empty($fields)) { @@ -172,22 +172,22 @@ class ItemDeliveryData return true; } - return DBA::update('item-delivery-data', $fields, ['iid' => $item_id], true); + return DBA::update('post-delivery-data', $fields, ['uri-id' => $uri_id], true); } /** - * Delete item delivery data + * Delete URI delivery data * - * @param integer $item_id + * @param integer $uri_id * @return bool * @throws \Exception */ - public static function delete($item_id) + public static function delete(int $uri_id) { - if (empty($item_id)) { - throw new BadMethodCallException('Empty item_id'); + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); } - return DBA::delete('item-delivery-data', ['iid' => $item_id]); + return DBA::delete('post-delivery-data', ['uri-id' => $uri_id]); } } diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index c8d218ac2..708f5335a 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -36,8 +36,8 @@ use Friendica\Model\Conversation; use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; -use Friendica\Model\ItemDeliveryData; use Friendica\Model\Mail; +use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Model\User; use Friendica\Network\Probe; @@ -252,19 +252,25 @@ class Diaspora * One of the parameters is a contact array. * This is done to avoid duplicates. * - * @param integer $thread The id of the thread - * @param array $contacts The previously fetched contacts + * @param array $parent The parent post + * @param array $contacts The previously fetched contacts * * @return array of relay servers * @throws \Exception */ - public static function participantsForThread($thread, array $contacts) + public static function participantsForThread(array $parent, array $contacts) { - $participation = DBA::select('participation-view', [], ['iid' => $thread]); + if (!in_array($parent['private'], [Item::PUBLIC, Item::UNLISTED])) { + return $contacts; + } - while ($contact = DBA::fetch($participation)) { - if (empty($contact['protocol'])) { - $contact['protocol'] = $contact['network']; + $items = Item::select(['author-id'], ['parent' => $parent['id']], ['group_by' => ['author-id']]); + while ($item = DBA::fetch($items)) { + $contact = DBA::selectFirst('contact', ['id', 'url', 'name', 'protocol', 'batch', 'network'], + ['id' => $item['author-id']]); + if (!DBA::isResult($contact)) { + // Shouldn't happen + continue; } $exists = false; @@ -275,11 +281,11 @@ class Diaspora } if (!$exists) { + Logger::info('Add participant to receiver list', ['item' => $parent['guid'], 'participant' => $contact['url']]); $contacts[] = $contact; } } - - DBA::close($participation); + DBA::close($items); return $contacts; } @@ -2251,18 +2257,32 @@ class Diaspora * @param array $importer Array of the importer user * @param object $data The message object * - * @return bool always true + * @return bool success * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ private static function receiveParticipation(array $importer, $data) { $author = strtolower(Strings::escapeTags(XML::unescape($data->author))); + $guid = Strings::escapeTags(XML::unescape($data->guid)); $parent_guid = Strings::escapeTags(XML::unescape($data->parent_guid)); - $contact_id = Contact::getIdForURL($author); - if (!$contact_id) { - Logger::log('Contact not found: '.$author); + $contact = self::allowedContactByHandle($importer, $author, true); + if (!$contact) { + return false; + } + + if (self::messageExists($importer["uid"], $guid)) { + return true; + } + + $parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); + if (!$parent_item) { + return false; + } + + if (!in_array($parent_item['private'], [Item::PUBLIC, Item::UNLISTED])) { + Logger::info('Item is not public, participation is ignored', ['parent_guid' => $parent_guid, 'guid' => $guid, 'author' => $author]); return false; } @@ -2272,36 +2292,48 @@ class Diaspora return false; } - $item = Item::selectFirst(['id'], ['guid' => $parent_guid, 'origin' => true, 'private' => [Item::PUBLIC, Item::UNLISTED]]); - if (!DBA::isResult($item)) { - Logger::log('Item not found, no origin or private: '.$parent_guid); - return false; - } + $author_contact = self::authorContactByUrl($contact, $person, $importer["uid"]); - $author_parts = explode('@', $author); - if (isset($author_parts[1])) { - $server = $author_parts[1]; - } else { - // Should never happen - $server = $author; - } + // Store participation + $datarray = []; - Logger::log('Received participation for ID: '.$item['id'].' - Contact: '.$contact_id.' - Server: '.$server, Logger::DEBUG); + $datarray["protocol"] = Conversation::PARCEL_DIASPORA; - if (!DBA::exists('participation', ['iid' => $item['id'], 'server' => $server])) { - DBA::insert('participation', ['iid' => $item['id'], 'cid' => $contact_id, 'fid' => $person['id'], 'server' => $server]); - } + $datarray["uid"] = $importer["uid"]; + $datarray["contact-id"] = $author_contact["cid"]; + $datarray["network"] = $author_contact["network"]; + + $datarray["owner-link"] = $datarray["author-link"] = $person["url"]; + $datarray["owner-id"] = $datarray["author-id"] = Contact::getIdForURL($person["url"], 0); + + $datarray["guid"] = $guid; + $datarray["uri"] = self::getUriFromGuid($author, $guid); + + $datarray["verb"] = Activity::FOLLOW; + $datarray["gravity"] = GRAVITY_ACTIVITY; + $datarray["parent-uri"] = $parent_item["uri"]; + + $datarray["object-type"] = Activity\ObjectType::NOTE; + + $datarray["body"] = Activity::FOLLOW; + + // Diaspora doesn't provide a date for a participation + $datarray["changed"] = $datarray["created"] = $datarray["edited"] = DateTimeFormat::utcNow(); + + $message_id = Item::insert($datarray); + + Logger::info('Participation stored', ['id' => $message_id, 'guid' => $guid, 'parent_guid' => $parent_guid, 'author' => $author]); // Send all existing comments and likes to the requesting server - $comments = Item::select(['id', 'parent', 'verb', 'self'], ['parent' => $item['id']]); + $comments = Item::select(['id', 'uri-id', 'parent'], ['parent' => $parent_item['id']]); while ($comment = Item::fetch($comments)) { if ($comment['id'] == $comment['parent']) { continue; } - Logger::info('Deliver participation', ['item' => $comment['id'], 'contact' => $contact_id]); - if (Worker::add(PRIORITY_HIGH, 'Delivery', Delivery::POST, $comment['id'], $contact_id)) { - ItemDeliveryData::incrementQueueCount($comment['id'], 1); + Logger::info('Deliver participation', ['item' => $comment['id'], 'contact' => $author_contact["cid"]]); + if (Worker::add(PRIORITY_HIGH, 'Delivery', Delivery::POST, $comment['id'], $author_contact["cid"])) { + Post\DeliveryData::incrementQueueCount($comment['uri-id'], 1); } } DBA::close($comments); diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 8ab14c078..cff50f08e 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -2081,11 +2081,9 @@ class OStatus XML::addElement($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes); } - $tags = Tag::getByURIId($item['uri-id']); - if (count($tags)) { - foreach ($tags as $tag) { - $mentioned[$tag['url']] = $tag['url']; - } + // uri-id isn't present for follow entry pseudo-items + foreach (Tag::getByURIId($item['uri-id'] ?? 0) as $tag) { + $mentioned[$tag['url']] = $tag['url']; } // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS) diff --git a/src/Worker/APDelivery.php b/src/Worker/APDelivery.php index 60752896f..609bb8888 100644 --- a/src/Worker/APDelivery.php +++ b/src/Worker/APDelivery.php @@ -23,7 +23,8 @@ namespace Friendica\Worker; use Friendica\Core\Logger; use Friendica\Core\Worker; -use Friendica\Model\ItemDeliveryData; +use Friendica\Model\Item; +use Friendica\Model\Post; use Friendica\Protocol\ActivityPub; use Friendica\Util\HTTPSignature; @@ -67,10 +68,13 @@ class APDelivery } } + // This should never fail and is temporariy (until the move to ) + $item = Item::selectFirst(['uri-id'], ['id' => $target_id]); + if (!$success && !Worker::defer() && in_array($cmd, [Delivery::POST])) { - ItemDeliveryData::incrementQueueFailed($target_id); + Post\DeliveryData::incrementQueueFailed($item['uri-id']); } elseif ($success && in_array($cmd, [Delivery::POST])) { - ItemDeliveryData::incrementQueueDone($target_id, ItemDeliveryData::ACTIVITYPUB); + Post\DeliveryData::incrementQueueDone($item['uri-id'], Post\DeliveryData::ACTIVITYPUB); } } } diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index 94a0f4902..0a865cb3a 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -58,14 +58,14 @@ class Delivery if ($cmd == self::MAIL) { $target_item = DBA::selectFirst('mail', [], ['id' => $target_id]); if (!DBA::isResult($target_item)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } $uid = $target_item['uid']; } elseif ($cmd == self::SUGGESTION) { $target_item = DBA::selectFirst('fsuggest', [], ['id' => $target_id]); if (!DBA::isResult($target_item)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } $uid = $target_item['uid']; @@ -75,7 +75,7 @@ class Delivery } else { $item = Model\Item::selectFirst(['parent'], ['id' => $target_id]); if (!DBA::isResult($item) || empty($item['parent'])) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } $parent_id = intval($item['parent']); @@ -97,13 +97,13 @@ class Delivery if (empty($target_item)) { Logger::log('Item ' . $target_id . "wasn't found. Quitting here."); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } if (empty($parent)) { Logger::log('Parent ' . $parent_id . ' for item ' . $target_id . "wasn't found. Quitting here."); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -113,7 +113,7 @@ class Delivery $uid = $target_item['uid']; } else { Logger::log('Only public users for item ' . $target_id, Logger::DEBUG); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -127,7 +127,7 @@ class Delivery if (!empty($contact_id) && Model\Contact::isArchived($contact_id)) { Logger::info('Contact is archived', ['id' => $contact_id, 'cmd' => $cmd, 'item' => $target_item['id']]); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -187,7 +187,7 @@ class Delivery $owner = Model\User::getOwnerDataById($uid); if (!DBA::isResult($owner)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -196,12 +196,12 @@ class Delivery ['id' => $contact_id, 'blocked' => false, 'pending' => false, 'self' => false] ); if (!DBA::isResult($contact)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } if (Network::isUrlBlocked($contact['url'])) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -242,16 +242,16 @@ class Delivery /** * Increased the "failed" counter in the item delivery data * - * @param string $cmd Command - * @param integer $id Item id + * @param string $cmd Command + * @param array $item Item array */ - private static function setFailedQueue(string $cmd, int $id) + private static function setFailedQueue(string $cmd, array $item) { if (!in_array($cmd, [Delivery::POST, Delivery::POKE])) { return; } - Model\ItemDeliveryData::incrementQueueFailed($id); + Model\Post\DeliveryData::incrementQueueFailed($item['uri-id'] ?? $item['id']); } /** @@ -335,13 +335,13 @@ class Delivery DFRN::import($atom, $target_importer); if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], Model\ItemDeliveryData::DFRN); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DFRN); } return; } - $protocol = Model\ItemDeliveryData::DFRN; + $protocol = Model\Post\DeliveryData::DFRN; // We don't have a relationship with contacts on a public post. // Se we transmit with the new method and via Diaspora as a fallback @@ -357,9 +357,9 @@ class Delivery if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { if (($deliver_status >= 200) && ($deliver_status <= 299)) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], $protocol); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], $protocol); } else { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } return; @@ -376,11 +376,11 @@ class Delivery if ($deliver_status < 200) { // Legacy DFRN $deliver_status = DFRN::deliver($owner, $contact, $atom); - $protocol = Model\ItemDeliveryData::LEGACY_DFRN; + $protocol = Model\Post\DeliveryData::LEGACY_DFRN; } } else { $deliver_status = DFRN::deliver($owner, $contact, $atom); - $protocol = Model\ItemDeliveryData::LEGACY_DFRN; + $protocol = Model\Post\DeliveryData::LEGACY_DFRN; } Logger::info('DFRN Delivery', ['cmd' => $cmd, 'url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id'], 'return' => $deliver_status]); @@ -390,7 +390,7 @@ class Delivery Model\Contact::unmarkForArchival($contact); if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], $protocol); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], $protocol); } } else { // The message could not be delivered. We mark the contact as "dead" @@ -398,7 +398,7 @@ class Delivery Logger::info('Delivery failed: defer message', ['id' => ($target_item['guid'] ?? '') ?: $target_item['id']]); if (!Worker::defer() && in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } } @@ -475,7 +475,7 @@ class Delivery Model\Contact::unmarkForArchival($contact); if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], Model\ItemDeliveryData::DIASPORA); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DIASPORA); } } else { // The message could not be delivered. We mark the contact as "dead" @@ -490,10 +490,10 @@ class Delivery Logger::info('Delivery failed: defer message', ['id' => ($target_item['guid'] ?? '') ?: $target_item['id']]); // defer message for redelivery if (!Worker::defer() && in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } elseif (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } } @@ -603,7 +603,7 @@ class Delivery Email::send($addr, $subject, $headers, $target_item); - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], Model\ItemDeliveryData::MAIL); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::MAIL); Logger::info('Delivered via mail', ['guid' => $target_item['guid'], 'to' => $addr, 'subject' => $subject]); } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 35a228fce..b93b1dd74 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -32,7 +32,7 @@ use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\Group; use Friendica\Model\Item; -use Friendica\Model\ItemDeliveryData; +use Friendica\Model\Post; use Friendica\Model\PushSubscriber; use Friendica\Model\User; use Friendica\Network\Probe; @@ -441,7 +441,7 @@ class Notifier // Fetch the participation list // The function will ensure that there are no duplicates - $relay_list = Diaspora::participantsForThread($target_id, $relay_list); + $relay_list = Diaspora::participantsForThread($parent, $relay_list); // Add the relay to the list, avoid duplicates. // Don't send community posts to the relay. Forum posts via the Diaspora protocol are looking ugly. @@ -573,7 +573,7 @@ class Notifier /// @TODO Redeliver/queue these items on failure, though there is no contact record $delivery_queue_count++; Salmon::slapper($owner, $url, $slap); - ItemDeliveryData::incrementQueueDone($target_id, ItemDeliveryData::OSTATUS); + Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Post\DeliveryData::OSTATUS); } } @@ -595,11 +595,11 @@ class Notifier // Workaround for pure connector posts if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { if ($delivery_queue_count == 0) { - ItemDeliveryData::incrementQueueDone($target_item['id']); + Post\DeliveryData::incrementQueueDone($target_item['uri-id']); $delivery_queue_count = 1; } - ItemDeliveryData::incrementQueueCount($target_item['id'], $delivery_queue_count); + Post\DeliveryData::incrementQueueCount($target_item['uri-id'], $delivery_queue_count); } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index a9b0682ef..c21488389 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -804,25 +804,6 @@ return [ "uri-id" => ["uri-id"] ] ], - "item-delivery-data" => [ - "comment" => "Delivery data for items", - "fields" => [ - "iid" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "relation" => ["item" => "id"], "comment" => "Item id"], - "postopts" => ["type" => "text", "comment" => "External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery"], - "inform" => ["type" => "mediumtext", "comment" => "Additional receivers of the linked item"], - "queue_count" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Initial number of delivery recipients, used as item.delivery_queue_count"], - "queue_done" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries, used as item.delivery_queue_done"], - "queue_failed" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of unsuccessful deliveries, used as item.delivery_queue_failed"], - "activitypub" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via ActivityPub"], - "dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via DFRN"], - "legacy_dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via legacy DFRN"], - "diaspora" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via Diaspora"], - "ostatus" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via OStatus"], - ], - "indexes" => [ - "PRIMARY" => ["iid"], - ] - ], "item-uri" => [ "comment" => "URI and GUID for items", "fields" => [ @@ -927,6 +908,8 @@ return [ "link" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "iid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], "comment" => "item.id"], "parent" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], "comment" => ""], + "uri-id" => ["type" => "int unsigned", "relation" => ["item-uri" => "id"], "comment" => "Item-uri id of the related post"], + "parent-uri-id" => ["type" => "int unsigned", "relation" => ["item-uri" => "id"], "comment" => "Item-uri id of the parent of the related post"], "seen" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "verb" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => ""], "otype" => ["type" => "varchar(10)", "not null" => "1", "default" => "", "comment" => ""], @@ -945,8 +928,8 @@ return [ "fields" => [ "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], "notify-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["notify" => "id"], "comment" => ""], - "master-parent-item" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], - "comment" => ""], + "master-parent-item" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], "comment" => ""], + "master-parent-uri-id" => ["type" => "int unsigned", "relation" => ["item-uri" => "id"], "comment" => "Item-uri id of the parent of the related post"], "parent-item" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""], "receiver-uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "relation" => ["user" => "uid"], "comment" => "User id"], @@ -1319,6 +1302,25 @@ return [ "uri-id" => ["tid"] ] ], + "post-delivery-data" => [ + "comment" => "Delivery data for items", + "fields" => [ + "uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "relation" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"], + "postopts" => ["type" => "text", "comment" => "External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery"], + "inform" => ["type" => "mediumtext", "comment" => "Additional receivers of the linked item"], + "queue_count" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Initial number of delivery recipients, used as item.delivery_queue_count"], + "queue_done" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries, used as item.delivery_queue_done"], + "queue_failed" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of unsuccessful deliveries, used as item.delivery_queue_failed"], + "activitypub" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via ActivityPub"], + "dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via DFRN"], + "legacy_dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via legacy DFRN"], + "diaspora" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via Diaspora"], + "ostatus" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via OStatus"], + ], + "indexes" => [ + "PRIMARY" => ["uri-id"], + ] + ], "post-tag" => [ "comment" => "post relation to tags", "fields" => [ diff --git a/static/dbview.config.php b/static/dbview.config.php index fa302b866..4d7eadc28 100755 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -201,20 +201,6 @@ return [ INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self` INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid`" ], - "participation-view" => [ - "fields" => [ - "iid" => ["participation", "iid"], - "id" => ["contact", "id"], - "url" => ["contact", "url"], - "name" => ["contact", "name"], - "protocol" => ["contact", "protocol"], - "batch" => "CASE `contact`.`batch` WHEN '' THEN `fcontact`.`batch` ELSE `contact`.`batch` END", - "network" => "CASE `fcontact`.`network` WHEN '' THEN `contact`.`network` ELSE `fcontact`.`network` END", - ], - "query" => "FROM `participation` - INNER JOIN `contact` ON `contact`.`id` = `participation`.`cid` AND NOT `contact`.`archive` - INNER JOIN `fcontact` ON `fcontact`.`id` = `participation`.`fid`" - ], "pending-view" => [ "fields" => [ "id" => ["register", "id"], diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index efcb32bec..fb11ae347 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -27,7 +27,7 @@ return [ 'photo', 'workerqueue', 'mail', - 'item-delivery-data', + 'post-delivery-data', // Base test config to avoid notice messages 'config' => [ [ diff --git a/view/js/perfect-scrollbar/LICENSE b/view/js/perfect-scrollbar/LICENSE new file mode 100644 index 000000000..8df97031a --- /dev/null +++ b/view/js/perfect-scrollbar/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Hyunje Alex Jun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/view/js/perfect-scrollbar/README.md b/view/js/perfect-scrollbar/README.md new file mode 100644 index 000000000..9b445dbf8 --- /dev/null +++ b/view/js/perfect-scrollbar/README.md @@ -0,0 +1,5 @@ +# Bower Package of perfect-scrollbar + +This is the [Bower](https://bower.io/) package of perfect-scrollbar. + +For details and usage please read more on [https://github.com/noraesae/perfect-scrollbar](https://github.com/noraesae/perfect-scrollbar). diff --git a/view/js/perfect-scrollbar/bower.json b/view/js/perfect-scrollbar/bower.json new file mode 100644 index 000000000..423477446 --- /dev/null +++ b/view/js/perfect-scrollbar/bower.json @@ -0,0 +1,18 @@ +{ + "name": "perfect-scrollbar", + "version": "0.6.16", + "homepage": "http://noraesae.github.io/perfect-scrollbar/", + "authors": [ + "Hyunje Jun " + ], + "description": "Minimalistic but perfect custom scrollbar plugin", + "main": [ + "css/perfect-scrollbar.css", + "js/perfect-scrollbar.js" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "bower_components" + ] +} diff --git a/view/js/perfect-scrollbar/css/perfect-scrollbar.css b/view/js/perfect-scrollbar/css/perfect-scrollbar.css new file mode 100644 index 000000000..6f03551ad --- /dev/null +++ b/view/js/perfect-scrollbar/css/perfect-scrollbar.css @@ -0,0 +1,113 @@ +/* perfect-scrollbar v0.6.16 */ +.ps-container { + -ms-touch-action: auto; + touch-action: auto; + overflow: hidden !important; + -ms-overflow-style: none; } + @supports (-ms-overflow-style: none) { + .ps-container { + overflow: auto !important; } } + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .ps-container { + overflow: auto !important; } } + .ps-container.ps-active-x > .ps-scrollbar-x-rail, + .ps-container.ps-active-y > .ps-scrollbar-y-rail { + display: block; + background-color: transparent; } + .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #999; + height: 11px; } + .ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #999; + width: 11px; } + .ps-container > .ps-scrollbar-x-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0; + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + bottom: 0px; + /* there must be 'bottom' for ps-scrollbar-x-rail */ + height: 15px; } + .ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x { + position: absolute; + /* please don't change 'position' */ + background-color: #aaa; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + bottom: 2px; + /* there must be 'bottom' for ps-scrollbar-x */ + height: 6px; } + .ps-container > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x, .ps-container > .ps-scrollbar-x-rail:active > .ps-scrollbar-x { + height: 11px; } + .ps-container > .ps-scrollbar-y-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0; + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + right: 0; + /* there must be 'right' for ps-scrollbar-y-rail */ + width: 15px; } + .ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y { + position: absolute; + /* please don't change 'position' */ + background-color: #aaa; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + right: 2px; + /* there must be 'right' for ps-scrollbar-y */ + width: 6px; } + .ps-container > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y, .ps-container > .ps-scrollbar-y-rail:active > .ps-scrollbar-y { + width: 11px; } + .ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #999; + height: 11px; } + .ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #999; + width: 11px; } + .ps-container:hover > .ps-scrollbar-x-rail, + .ps-container:hover > .ps-scrollbar-y-rail { + opacity: 0.6; } + .ps-container:hover > .ps-scrollbar-x-rail:hover { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x { + background-color: #999; } + .ps-container:hover > .ps-scrollbar-y-rail:hover { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y { + background-color: #999; } diff --git a/view/js/perfect-scrollbar/css/perfect-scrollbar.min.css b/view/js/perfect-scrollbar/css/perfect-scrollbar.min.css new file mode 100644 index 000000000..a0ad63ca5 --- /dev/null +++ b/view/js/perfect-scrollbar/css/perfect-scrollbar.min.css @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.16 */ +.ps-container{-ms-touch-action:auto;touch-action:auto;overflow:hidden !important;-ms-overflow-style:none}@supports (-ms-overflow-style: none){.ps-container{overflow:auto !important}}@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){.ps-container{overflow:auto !important}}.ps-container.ps-active-x>.ps-scrollbar-x-rail,.ps-container.ps-active-y>.ps-scrollbar-y-rail{display:block;background-color:transparent}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999;height:11px}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999;width:11px}.ps-container>.ps-scrollbar-x-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;bottom:0px;height:15px}.ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;bottom:2px;height:6px}.ps-container>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x,.ps-container>.ps-scrollbar-x-rail:active>.ps-scrollbar-x{height:11px}.ps-container>.ps-scrollbar-y-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;right:0;width:15px}.ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;right:2px;width:6px}.ps-container>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y,.ps-container>.ps-scrollbar-y-rail:active>.ps-scrollbar-y{width:11px}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999;height:11px}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999;width:11px}.ps-container:hover>.ps-scrollbar-x-rail,.ps-container:hover>.ps-scrollbar-y-rail{opacity:.6}.ps-container:hover>.ps-scrollbar-x-rail:hover{background-color:#eee;opacity:.9}.ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x{background-color:#999}.ps-container:hover>.ps-scrollbar-y-rail:hover{background-color:#eee;opacity:.9}.ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y{background-color:#999} diff --git a/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.js new file mode 100644 index 000000000..5ae830f12 --- /dev/null +++ b/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.js @@ -0,0 +1,1577 @@ +/* perfect-scrollbar v0.6.16 */ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { + classes.splice(idx, 1); + } + element.className = classes.join(' '); +} + +exports.add = function (element, className) { + if (element.classList) { + element.classList.add(className); + } else { + oldAdd(element, className); + } +}; + +exports.remove = function (element, className) { + if (element.classList) { + element.classList.remove(className); + } else { + oldRemove(element, className); + } +}; + +exports.list = function (element) { + if (element.classList) { + return Array.prototype.slice.apply(element.classList); + } else { + return element.className.split(' '); + } +}; + +},{}],3:[function(require,module,exports){ +'use strict'; + +var DOM = {}; + +DOM.e = function (tagName, className) { + var element = document.createElement(tagName); + element.className = className; + return element; +}; + +DOM.appendTo = function (child, parent) { + parent.appendChild(child); + return child; +}; + +function cssGet(element, styleName) { + return window.getComputedStyle(element)[styleName]; +} + +function cssSet(element, styleName, styleValue) { + if (typeof styleValue === 'number') { + styleValue = styleValue.toString() + 'px'; + } + element.style[styleName] = styleValue; + return element; +} + +function cssMultiSet(element, obj) { + for (var key in obj) { + var val = obj[key]; + if (typeof val === 'number') { + val = val.toString() + 'px'; + } + element.style[key] = val; + } + return element; +} + +DOM.css = function (element, styleNameOrObject, styleValue) { + if (typeof styleNameOrObject === 'object') { + // multiple set with object + return cssMultiSet(element, styleNameOrObject); + } else { + if (typeof styleValue === 'undefined') { + return cssGet(element, styleNameOrObject); + } else { + return cssSet(element, styleNameOrObject, styleValue); + } + } +}; + +DOM.matches = function (element, query) { + if (typeof element.matches !== 'undefined') { + return element.matches(query); + } else { + if (typeof element.matchesSelector !== 'undefined') { + return element.matchesSelector(query); + } else if (typeof element.webkitMatchesSelector !== 'undefined') { + return element.webkitMatchesSelector(query); + } else if (typeof element.mozMatchesSelector !== 'undefined') { + return element.mozMatchesSelector(query); + } else if (typeof element.msMatchesSelector !== 'undefined') { + return element.msMatchesSelector(query); + } + } +}; + +DOM.remove = function (element) { + if (typeof element.remove !== 'undefined') { + element.remove(); + } else { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + } +}; + +DOM.queryChildren = function (element, selector) { + return Array.prototype.filter.call(element.childNodes, function (child) { + return DOM.matches(child, selector); + }); +}; + +module.exports = DOM; + +},{}],4:[function(require,module,exports){ +'use strict'; + +var EventElement = function (element) { + this.element = element; + this.events = {}; +}; + +EventElement.prototype.bind = function (eventName, handler) { + if (typeof this.events[eventName] === 'undefined') { + this.events[eventName] = []; + } + this.events[eventName].push(handler); + this.element.addEventListener(eventName, handler, false); +}; + +EventElement.prototype.unbind = function (eventName, handler) { + var isHandlerProvided = (typeof handler !== 'undefined'); + this.events[eventName] = this.events[eventName].filter(function (hdlr) { + if (isHandlerProvided && hdlr !== handler) { + return true; + } + this.element.removeEventListener(eventName, hdlr, false); + return false; + }, this); +}; + +EventElement.prototype.unbindAll = function () { + for (var name in this.events) { + this.unbind(name); + } +}; + +var EventManager = function () { + this.eventElements = []; +}; + +EventManager.prototype.eventElement = function (element) { + var ee = this.eventElements.filter(function (eventElement) { + return eventElement.element === element; + })[0]; + if (typeof ee === 'undefined') { + ee = new EventElement(element); + this.eventElements.push(ee); + } + return ee; +}; + +EventManager.prototype.bind = function (element, eventName, handler) { + this.eventElement(element).bind(eventName, handler); +}; + +EventManager.prototype.unbind = function (element, eventName, handler) { + this.eventElement(element).unbind(eventName, handler); +}; + +EventManager.prototype.unbindAll = function () { + for (var i = 0; i < this.eventElements.length; i++) { + this.eventElements[i].unbindAll(); + } +}; + +EventManager.prototype.once = function (element, eventName, handler) { + var ee = this.eventElement(element); + var onceHandler = function (e) { + ee.unbind(eventName, onceHandler); + handler(e); + }; + ee.bind(eventName, onceHandler); +}; + +module.exports = EventManager; + +},{}],5:[function(require,module,exports){ +'use strict'; + +module.exports = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; +})(); + +},{}],6:[function(require,module,exports){ +'use strict'; + +var cls = require('./class'); +var dom = require('./dom'); + +var toInt = exports.toInt = function (x) { + return parseInt(x, 10) || 0; +}; + +var clone = exports.clone = function (obj) { + if (!obj) { + return null; + } else if (obj.constructor === Array) { + return obj.map(clone); + } else if (typeof obj === 'object') { + var result = {}; + for (var key in obj) { + result[key] = clone(obj[key]); + } + return result; + } else { + return obj; + } +}; + +exports.extend = function (original, source) { + var result = clone(original); + for (var key in source) { + result[key] = clone(source[key]); + } + return result; +}; + +exports.isEditable = function (el) { + return dom.matches(el, "input,[contenteditable]") || + dom.matches(el, "select,[contenteditable]") || + dom.matches(el, "textarea,[contenteditable]") || + dom.matches(el, "button,[contenteditable]"); +}; + +exports.removePsClasses = function (element) { + var clsList = cls.list(element); + for (var i = 0; i < clsList.length; i++) { + var className = clsList[i]; + if (className.indexOf('ps-') === 0) { + cls.remove(element, className); + } + } +}; + +exports.outerWidth = function (element) { + return toInt(dom.css(element, 'width')) + + toInt(dom.css(element, 'paddingLeft')) + + toInt(dom.css(element, 'paddingRight')) + + toInt(dom.css(element, 'borderLeftWidth')) + + toInt(dom.css(element, 'borderRightWidth')); +}; + +exports.startScrolling = function (element, axis) { + cls.add(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.add(element, 'ps-' + axis); + } else { + cls.add(element, 'ps-x'); + cls.add(element, 'ps-y'); + } +}; + +exports.stopScrolling = function (element, axis) { + cls.remove(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.remove(element, 'ps-' + axis); + } else { + cls.remove(element, 'ps-x'); + cls.remove(element, 'ps-y'); + } +}; + +exports.env = { + isWebKit: 'WebkitAppearance' in document.documentElement.style, + supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + supportsIePointer: window.navigator.msMaxTouchPoints !== null +}; + +},{"./class":2,"./dom":3}],7:[function(require,module,exports){ +'use strict'; + +var destroy = require('./plugin/destroy'); +var initialize = require('./plugin/initialize'); +var update = require('./plugin/update'); + +module.exports = { + initialize: initialize, + update: update, + destroy: destroy +}; + +},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(require,module,exports){ +'use strict'; + +module.exports = { + handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'], + maxScrollbarLength: null, + minScrollbarLength: null, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + suppressScrollX: false, + suppressScrollY: false, + swipePropagation: true, + useBothWheelAxes: false, + wheelPropagation: false, + wheelSpeed: 1, + theme: 'default' +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + i.event.unbindAll(); + dom.remove(i.scrollbarX); + dom.remove(i.scrollbarY); + dom.remove(i.scrollbarXRail); + dom.remove(i.scrollbarYRail); + _.removePsClasses(element); + + instances.remove(element); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindClickRailHandler(element, i) { + function pageOffset(el) { + return el.getBoundingClientRect(); + } + var stopPropagation = function (e) { e.stopPropagation(); }; + + i.event.bind(i.scrollbarY, 'click', stopPropagation); + i.event.bind(i.scrollbarYRail, 'click', function (e) { + var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top; + var direction = positionTop > i.scrollbarYTop ? 1 : -1; + + updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight); + updateGeometry(element); + + e.stopPropagation(); + }); + + i.event.bind(i.scrollbarX, 'click', stopPropagation); + i.event.bind(i.scrollbarXRail, 'click', function (e) { + var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left; + var direction = positionLeft > i.scrollbarXLeft ? 1 : -1; + + updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth); + updateGeometry(element); + + e.stopPropagation(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindClickRailHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseScrollXHandler(element, i) { + var currentLeft = null; + var currentPageX = null; + + function updateScrollLeft(deltaX) { + var newLeft = currentLeft + (deltaX * i.railXRatio); + var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth)); + + if (newLeft < 0) { + i.scrollbarXLeft = 0; + } else if (newLeft > maxLeft) { + i.scrollbarXLeft = maxLeft; + } else { + i.scrollbarXLeft = newLeft; + } + + var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment; + updateScroll(element, 'left', scrollLeft); + } + + var mouseMoveHandler = function (e) { + updateScrollLeft(e.pageX - currentPageX); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'x'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarX, 'mousedown', function (e) { + currentPageX = e.pageX; + currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio; + _.startScrolling(element, 'x'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +function bindMouseScrollYHandler(element, i) { + var currentTop = null; + var currentPageY = null; + + function updateScrollTop(deltaY) { + var newTop = currentTop + (deltaY * i.railYRatio); + var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight)); + + if (newTop < 0) { + i.scrollbarYTop = 0; + } else if (newTop > maxTop) { + i.scrollbarYTop = maxTop; + } else { + i.scrollbarYTop = newTop; + } + + var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight))); + updateScroll(element, 'top', scrollTop); + } + + var mouseMoveHandler = function (e) { + updateScrollTop(e.pageY - currentPageY); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'y'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarY, 'mousedown', function (e) { + currentPageY = e.pageY; + currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio; + _.startScrolling(element, 'y'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseScrollXHandler(element, i); + bindMouseScrollYHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindKeyboardHandler(element, i) { + var hovered = false; + i.event.bind(element, 'mouseenter', function () { + hovered = true; + }); + i.event.bind(element, 'mouseleave', function () { + hovered = false; + }); + + var shouldPrevent = false; + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + i.event.bind(i.ownerDocument, 'keydown', function (e) { + if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) { + return; + } + + var focused = dom.matches(i.scrollbarX, ':focus') || + dom.matches(i.scrollbarY, ':focus'); + + if (!hovered && !focused) { + return; + } + + var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement; + if (activeElement) { + if (activeElement.tagName === 'IFRAME') { + activeElement = activeElement.contentDocument.activeElement; + } else { + // go deeper if element is a webcomponent + while (activeElement.shadowRoot) { + activeElement = activeElement.shadowRoot.activeElement; + } + } + if (_.isEditable(activeElement)) { + return; + } + } + + var deltaX = 0; + var deltaY = 0; + + switch (e.which) { + case 37: // left + if (e.metaKey) { + deltaX = -i.contentWidth; + } else if (e.altKey) { + deltaX = -i.containerWidth; + } else { + deltaX = -30; + } + break; + case 38: // up + if (e.metaKey) { + deltaY = i.contentHeight; + } else if (e.altKey) { + deltaY = i.containerHeight; + } else { + deltaY = 30; + } + break; + case 39: // right + if (e.metaKey) { + deltaX = i.contentWidth; + } else if (e.altKey) { + deltaX = i.containerWidth; + } else { + deltaX = 30; + } + break; + case 40: // down + if (e.metaKey) { + deltaY = -i.contentHeight; + } else if (e.altKey) { + deltaY = -i.containerHeight; + } else { + deltaY = -30; + } + break; + case 33: // page up + deltaY = 90; + break; + case 32: // space bar + if (e.shiftKey) { + deltaY = 90; + } else { + deltaY = -90; + } + break; + case 34: // page down + deltaY = -90; + break; + case 35: // end + if (e.ctrlKey) { + deltaY = -i.contentHeight; + } else { + deltaY = -i.containerHeight; + } + break; + case 36: // home + if (e.ctrlKey) { + deltaY = element.scrollTop; + } else { + deltaY = i.containerHeight; + } + break; + default: + return; + } + + updateScroll(element, 'top', element.scrollTop - deltaY); + updateScroll(element, 'left', element.scrollLeft + deltaX); + updateGeometry(element); + + shouldPrevent = shouldPreventDefault(deltaX, deltaY); + if (shouldPrevent) { + e.preventDefault(); + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindKeyboardHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseWheelHandler(element, i) { + var shouldPrevent = false; + + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + function getDeltaFromEvent(e) { + var deltaX = e.deltaX; + var deltaY = -1 * e.deltaY; + + if (typeof deltaX === "undefined" || typeof deltaY === "undefined") { + // OS X Safari + deltaX = -1 * e.wheelDeltaX / 6; + deltaY = e.wheelDeltaY / 6; + } + + if (e.deltaMode && e.deltaMode === 1) { + // Firefox in deltaMode 1: Line scrolling + deltaX *= 10; + deltaY *= 10; + } + + if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) { + // IE in some mouse drivers + deltaX = 0; + deltaY = e.wheelDelta; + } + + if (e.shiftKey) { + // reverse axis with shift key + return [-deltaY, -deltaX]; + } + return [deltaX, deltaY]; + } + + function shouldBeConsumedByChild(deltaX, deltaY) { + var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover'); + if (child) { + if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) { + // if not scrollable + return false; + } + + var maxScrollTop = child.scrollHeight - child.clientHeight; + if (maxScrollTop > 0) { + if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) { + return true; + } + } + var maxScrollLeft = child.scrollLeft - child.clientWidth; + if (maxScrollLeft > 0) { + if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) { + return true; + } + } + } + return false; + } + + function mousewheelHandler(e) { + var delta = getDeltaFromEvent(e); + + var deltaX = delta[0]; + var deltaY = delta[1]; + + if (shouldBeConsumedByChild(deltaX, deltaY)) { + return; + } + + shouldPrevent = false; + if (!i.settings.useBothWheelAxes) { + // deltaX will only be used for horizontal scrolling and deltaY will + // only be used for vertical scrolling - this is the default + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else if (i.scrollbarYActive && !i.scrollbarXActive) { + // only vertical scrollbar is active and useBothWheelAxes option is + // active, so let's scroll vertical bar using both mouse wheel axes + if (deltaY) { + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } else if (i.scrollbarXActive && !i.scrollbarYActive) { + // useBothWheelAxes and only horizontal bar is active, so use both + // wheel axes for horizontal bar + if (deltaX) { + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } + + updateGeometry(element); + + shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY)); + if (shouldPrevent) { + e.stopPropagation(); + e.preventDefault(); + } + } + + if (typeof window.onwheel !== "undefined") { + i.event.bind(element, 'wheel', mousewheelHandler); + } else if (typeof window.onmousewheel !== "undefined") { + i.event.bind(element, 'mousewheel', mousewheelHandler); + } +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseWheelHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); + +function bindNativeScrollHandler(element, i) { + i.event.bind(element, 'scroll', function () { + updateGeometry(element); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindNativeScrollHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19}],15:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindSelectionHandler(element, i) { + function getRangeNode() { + var selection = window.getSelection ? window.getSelection() : + document.getSelection ? document.getSelection() : ''; + if (selection.toString().length === 0) { + return null; + } else { + return selection.getRangeAt(0).commonAncestorContainer; + } + } + + var scrollingLoop = null; + var scrollDiff = {top: 0, left: 0}; + function startScrolling() { + if (!scrollingLoop) { + scrollingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(scrollingLoop); + return; + } + + updateScroll(element, 'top', element.scrollTop + scrollDiff.top); + updateScroll(element, 'left', element.scrollLeft + scrollDiff.left); + updateGeometry(element); + }, 50); // every .1 sec + } + } + function stopScrolling() { + if (scrollingLoop) { + clearInterval(scrollingLoop); + scrollingLoop = null; + } + _.stopScrolling(element); + } + + var isSelected = false; + i.event.bind(i.ownerDocument, 'selectionchange', function () { + if (element.contains(getRangeNode())) { + isSelected = true; + } else { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'mouseup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'keyup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + + i.event.bind(window, 'mousemove', function (e) { + if (isSelected) { + var mousePosition = {x: e.pageX, y: e.pageY}; + var containerGeometry = { + left: element.offsetLeft, + right: element.offsetLeft + element.offsetWidth, + top: element.offsetTop, + bottom: element.offsetTop + element.offsetHeight + }; + + if (mousePosition.x < containerGeometry.left + 3) { + scrollDiff.left = -5; + _.startScrolling(element, 'x'); + } else if (mousePosition.x > containerGeometry.right - 3) { + scrollDiff.left = 5; + _.startScrolling(element, 'x'); + } else { + scrollDiff.left = 0; + } + + if (mousePosition.y < containerGeometry.top + 3) { + if (containerGeometry.top + 3 - mousePosition.y < 5) { + scrollDiff.top = -5; + } else { + scrollDiff.top = -20; + } + _.startScrolling(element, 'y'); + } else if (mousePosition.y > containerGeometry.bottom - 3) { + if (mousePosition.y - containerGeometry.bottom + 3 < 5) { + scrollDiff.top = 5; + } else { + scrollDiff.top = 20; + } + _.startScrolling(element, 'y'); + } else { + scrollDiff.top = 0; + } + + if (scrollDiff.top === 0 && scrollDiff.left === 0) { + stopScrolling(); + } else { + startScrolling(); + } + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindSelectionHandler(element, i); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindTouchHandler(element, i, supportsTouch, supportsIePointer) { + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + var scrollLeft = element.scrollLeft; + var magnitudeX = Math.abs(deltaX); + var magnitudeY = Math.abs(deltaY); + + if (magnitudeY > magnitudeX) { + // user is perhaps trying to swipe up/down the page + + if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) || + ((deltaY > 0) && (scrollTop === 0))) { + return !i.settings.swipePropagation; + } + } else if (magnitudeX > magnitudeY) { + // user is perhaps trying to swipe left/right across the page + + if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) || + ((deltaX > 0) && (scrollLeft === 0))) { + return !i.settings.swipePropagation; + } + } + + return true; + } + + function applyTouchMove(differenceX, differenceY) { + updateScroll(element, 'top', element.scrollTop - differenceY); + updateScroll(element, 'left', element.scrollLeft - differenceX); + + updateGeometry(element); + } + + var startOffset = {}; + var startTime = 0; + var speed = {}; + var easingLoop = null; + var inGlobalTouch = false; + var inLocalTouch = false; + + function globalTouchStart() { + inGlobalTouch = true; + } + function globalTouchEnd() { + inGlobalTouch = false; + } + + function getTouch(e) { + if (e.targetTouches) { + return e.targetTouches[0]; + } else { + // Maybe IE pointer + return e; + } + } + function shouldHandle(e) { + if (e.targetTouches && e.targetTouches.length === 1) { + return true; + } + if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + return true; + } + return false; + } + function touchStart(e) { + if (shouldHandle(e)) { + inLocalTouch = true; + + var touch = getTouch(e); + + startOffset.pageX = touch.pageX; + startOffset.pageY = touch.pageY; + + startTime = (new Date()).getTime(); + + if (easingLoop !== null) { + clearInterval(easingLoop); + } + + e.stopPropagation(); + } + } + function touchMove(e) { + if (!inLocalTouch && i.settings.swipePropagation) { + touchStart(e); + } + if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) { + var touch = getTouch(e); + + var currentOffset = {pageX: touch.pageX, pageY: touch.pageY}; + + var differenceX = currentOffset.pageX - startOffset.pageX; + var differenceY = currentOffset.pageY - startOffset.pageY; + + applyTouchMove(differenceX, differenceY); + startOffset = currentOffset; + + var currentTime = (new Date()).getTime(); + + var timeGap = currentTime - startTime; + if (timeGap > 0) { + speed.x = differenceX / timeGap; + speed.y = differenceY / timeGap; + startTime = currentTime; + } + + if (shouldPreventDefault(differenceX, differenceY)) { + e.stopPropagation(); + e.preventDefault(); + } + } + } + function touchEnd() { + if (!inGlobalTouch && inLocalTouch) { + inLocalTouch = false; + + clearInterval(easingLoop); + easingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(easingLoop); + return; + } + + if (!speed.x && !speed.y) { + clearInterval(easingLoop); + return; + } + + if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(easingLoop); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + } + } + + if (supportsTouch) { + i.event.bind(window, 'touchstart', globalTouchStart); + i.event.bind(window, 'touchend', globalTouchEnd); + i.event.bind(element, 'touchstart', touchStart); + i.event.bind(element, 'touchmove', touchMove); + i.event.bind(element, 'touchend', touchEnd); + } else if (supportsIePointer) { + if (window.PointerEvent) { + i.event.bind(window, 'pointerdown', globalTouchStart); + i.event.bind(window, 'pointerup', globalTouchEnd); + i.event.bind(element, 'pointerdown', touchStart); + i.event.bind(element, 'pointermove', touchMove); + i.event.bind(element, 'pointerup', touchEnd); + } else if (window.MSPointerEvent) { + i.event.bind(window, 'MSPointerDown', globalTouchStart); + i.event.bind(window, 'MSPointerUp', globalTouchEnd); + i.event.bind(element, 'MSPointerDown', touchStart); + i.event.bind(element, 'MSPointerMove', touchMove); + i.event.bind(element, 'MSPointerUp', touchEnd); + } + } +} + +module.exports = function (element) { + if (!_.env.supportsTouch && !_.env.supportsIePointer) { + return; + } + + var i = instances.get(element); + bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); + +// Handlers +var handlers = { + 'click-rail': require('./handler/click-rail'), + 'drag-scrollbar': require('./handler/drag-scrollbar'), + 'keyboard': require('./handler/keyboard'), + 'wheel': require('./handler/mouse-wheel'), + 'touch': require('./handler/touch'), + 'selection': require('./handler/selection') +}; +var nativeScrollHandler = require('./handler/native-scroll'); + +module.exports = function (element, userSettings) { + userSettings = typeof userSettings === 'object' ? userSettings : {}; + + cls.add(element, 'ps-container'); + + // Create a plugin instance. + var i = instances.add(element); + + i.settings = _.extend(i.settings, userSettings); + cls.add(element, 'ps-theme-' + i.settings.theme); + + i.settings.handlers.forEach(function (handlerName) { + handlers[handlerName](element); + }); + + nativeScrollHandler(element); + + updateGeometry(element); +}; + +},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var defaultSettings = require('./default-setting'); +var dom = require('../lib/dom'); +var EventManager = require('../lib/event-manager'); +var guid = require('../lib/guid'); + +var instances = {}; + +function Instance(element) { + var i = this; + + i.settings = _.clone(defaultSettings); + i.containerWidth = null; + i.containerHeight = null; + i.contentWidth = null; + i.contentHeight = null; + + i.isRtl = dom.css(element, 'direction') === "rtl"; + i.isNegativeScroll = (function () { + var originalScrollLeft = element.scrollLeft; + var result = null; + element.scrollLeft = -1; + result = element.scrollLeft < 0; + element.scrollLeft = originalScrollLeft; + return result; + })(); + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + i.event = new EventManager(); + i.ownerDocument = element.ownerDocument || document; + + function focus() { + cls.add(element, 'ps-focus'); + } + + function blur() { + cls.remove(element, 'ps-focus'); + } + + i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element); + i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail); + i.scrollbarX.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarX, 'focus', focus); + i.event.bind(i.scrollbarX, 'blur', blur); + i.scrollbarXActive = null; + i.scrollbarXWidth = null; + i.scrollbarXLeft = null; + i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom')); + i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN + i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top')); + i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth')); + // Set rail to display:block to calculate margins + dom.css(i.scrollbarXRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + dom.css(i.scrollbarXRail, 'display', ''); + i.railXWidth = null; + i.railXRatio = null; + + i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element); + i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail); + i.scrollbarY.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarY, 'focus', focus); + i.event.bind(i.scrollbarY, 'blur', blur); + i.scrollbarYActive = null; + i.scrollbarYHeight = null; + i.scrollbarYTop = null; + i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right')); + i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN + i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left')); + i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null; + i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth')); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + dom.css(i.scrollbarYRail, 'display', ''); + i.railYHeight = null; + i.railYRatio = null; +} + +function getId(element) { + return element.getAttribute('data-ps-id'); +} + +function setId(element, id) { + element.setAttribute('data-ps-id', id); +} + +function removeId(element) { + element.removeAttribute('data-ps-id'); +} + +exports.add = function (element) { + var newId = guid(); + setId(element, newId); + instances[newId] = new Instance(element); + return instances[newId]; +}; + +exports.remove = function (element) { + delete instances[getId(element)]; + removeId(element); +}; + +exports.get = function (element) { + return instances[getId(element)]; +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateScroll = require('./update-scroll'); + +function getThumbSize(i, thumbSize) { + if (i.settings.minScrollbarLength) { + thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength); + } + if (i.settings.maxScrollbarLength) { + thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength); + } + return thumbSize; +} + +function updateCss(element, i) { + var xRailOffset = {width: i.railXWidth}; + if (i.isRtl) { + xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth; + } else { + xRailOffset.left = element.scrollLeft; + } + if (i.isScrollbarXUsingBottom) { + xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop; + } else { + xRailOffset.top = i.scrollbarXTop + element.scrollTop; + } + dom.css(i.scrollbarXRail, xRailOffset); + + var yRailOffset = {top: element.scrollTop, height: i.railYHeight}; + if (i.isScrollbarYUsingRight) { + if (i.isRtl) { + yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth; + } else { + yRailOffset.right = i.scrollbarYRight - element.scrollLeft; + } + } else { + if (i.isRtl) { + yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth; + } else { + yRailOffset.left = i.scrollbarYLeft + element.scrollLeft; + } + } + dom.css(i.scrollbarYRail, yRailOffset); + + dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth}); + dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth}); +} + +module.exports = function (element) { + var i = instances.get(element); + + i.containerWidth = element.clientWidth; + i.containerHeight = element.clientHeight; + i.contentWidth = element.scrollWidth; + i.contentHeight = element.scrollHeight; + + var existingRails; + if (!element.contains(i.scrollbarXRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarXRail, element); + } + if (!element.contains(i.scrollbarYRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarYRail, element); + } + + if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) { + i.scrollbarXActive = true; + i.railXWidth = i.containerWidth - i.railXMarginWidth; + i.railXRatio = i.containerWidth / i.railXWidth; + i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth)); + i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth)); + } else { + i.scrollbarXActive = false; + } + + if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) { + i.scrollbarYActive = true; + i.railYHeight = i.containerHeight - i.railYMarginHeight; + i.railYRatio = i.containerHeight / i.railYHeight; + i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight)); + i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight)); + } else { + i.scrollbarYActive = false; + } + + if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) { + i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth; + } + if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) { + i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight; + } + + updateCss(element, i); + + if (i.scrollbarXActive) { + cls.add(element, 'ps-active-x'); + } else { + cls.remove(element, 'ps-active-x'); + i.scrollbarXWidth = 0; + i.scrollbarXLeft = 0; + updateScroll(element, 'left', 0); + } + if (i.scrollbarYActive) { + cls.add(element, 'ps-active-y'); + } else { + cls.remove(element, 'ps-active-y'); + i.scrollbarYHeight = 0; + i.scrollbarYTop = 0; + updateScroll(element, 'top', 0); + } +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(require,module,exports){ +'use strict'; + +var instances = require('./instances'); + +var lastTop; +var lastLeft; + +var createDOMEvent = function (name) { + var event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; +}; + +module.exports = function (element, axis, value) { + if (typeof element === 'undefined') { + throw 'You must provide an element to the update-scroll function'; + } + + if (typeof axis === 'undefined') { + throw 'You must provide an axis to the update-scroll function'; + } + + if (typeof value === 'undefined') { + throw 'You must provide a value to the update-scroll function'; + } + + if (axis === 'top' && value <= 0) { + element.scrollTop = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-y-reach-start')); + } + + if (axis === 'left' && value <= 0) { + element.scrollLeft = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-x-reach-start')); + } + + var i = instances.get(element); + + if (axis === 'top' && value >= i.contentHeight - i.containerHeight) { + // don't allow scroll past container + value = i.contentHeight - i.containerHeight; + if (value - element.scrollTop <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollTop; + } else { + element.scrollTop = value; + } + element.dispatchEvent(createDOMEvent('ps-y-reach-end')); + } + + if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { + // don't allow scroll past container + value = i.contentWidth - i.containerWidth; + if (value - element.scrollLeft <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollLeft; + } else { + element.scrollLeft = value; + } + element.dispatchEvent(createDOMEvent('ps-x-reach-end')); + } + + if (!lastTop) { + lastTop = element.scrollTop; + } + + if (!lastLeft) { + lastLeft = element.scrollLeft; + } + + if (axis === 'top' && value < lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-up')); + } + + if (axis === 'top' && value > lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-down')); + } + + if (axis === 'left' && value < lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-left')); + } + + if (axis === 'left' && value > lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-right')); + } + + if (axis === 'top') { + element.scrollTop = lastTop = value; + element.dispatchEvent(createDOMEvent('ps-scroll-y')); + } + + if (axis === 'left') { + element.scrollLeft = lastLeft = value; + element.dispatchEvent(createDOMEvent('ps-scroll-x')); + } + +}; + +},{"./instances":18}],21:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); +var updateScroll = require('./update-scroll'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + // Recalcuate negative scrollLeft adjustment + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + + // Recalculate rail margins + dom.css(i.scrollbarXRail, 'display', 'block'); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + + // Hide scrollbars not to affect scrollWidth and scrollHeight + dom.css(i.scrollbarXRail, 'display', 'none'); + dom.css(i.scrollbarYRail, 'display', 'none'); + + updateGeometry(element); + + // Update top/left scroll to trigger events + updateScroll(element, 'top', element.scrollTop); + updateScroll(element, 'left', element.scrollLeft); + + dom.css(i.scrollbarXRail, 'display', ''); + dom.css(i.scrollbarYRail, 'display', ''); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); diff --git a/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js new file mode 100644 index 000000000..01bc76888 --- /dev/null +++ b/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.16 */ +!function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var a="function"==typeof require&&require;if(!s&&a)return a(i,!0);if(l)return l(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[i]={exports:{}};e[i][0].call(u.exports,function(t){var n=e[i][1][t];return o(n?n:t)},u,u.exports,t,e,n,r)}return n[i].exports}for(var l="function"==typeof require&&require,i=0;i=0&&n.splice(r,1),t.className=n.join(" ")}n.add=function(t,e){t.classList?t.classList.add(e):r(t,e)},n.remove=function(t,e){t.classList?t.classList.remove(e):o(t,e)},n.list=function(t){return t.classList?Array.prototype.slice.apply(t.classList):t.className.split(" ")}},{}],3:[function(t,e,n){"use strict";function r(t,e){return window.getComputedStyle(t)[e]}function o(t,e,n){return"number"==typeof n&&(n=n.toString()+"px"),t.style[e]=n,t}function l(t,e){for(var n in e){var r=e[n];"number"==typeof r&&(r=r.toString()+"px"),t.style[n]=r}return t}var i={};i.e=function(t,e){var n=document.createElement(t);return n.className=e,n},i.appendTo=function(t,e){return e.appendChild(t),t},i.css=function(t,e,n){return"object"==typeof e?l(t,e):"undefined"==typeof n?r(t,e):o(t,e,n)},i.matches=function(t,e){return"undefined"!=typeof t.matches?t.matches(e):"undefined"!=typeof t.matchesSelector?t.matchesSelector(e):"undefined"!=typeof t.webkitMatchesSelector?t.webkitMatchesSelector(e):"undefined"!=typeof t.mozMatchesSelector?t.mozMatchesSelector(e):"undefined"!=typeof t.msMatchesSelector?t.msMatchesSelector(e):void 0},i.remove=function(t){"undefined"!=typeof t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)},i.queryChildren=function(t,e){return Array.prototype.filter.call(t.childNodes,function(t){return i.matches(t,e)})},e.exports=i},{}],4:[function(t,e,n){"use strict";var r=function(t){this.element=t,this.events={}};r.prototype.bind=function(t,e){"undefined"==typeof this.events[t]&&(this.events[t]=[]),this.events[t].push(e),this.element.addEventListener(t,e,!1)},r.prototype.unbind=function(t,e){var n="undefined"!=typeof e;this.events[t]=this.events[t].filter(function(r){return!(!n||r===e)||(this.element.removeEventListener(t,r,!1),!1)},this)},r.prototype.unbindAll=function(){for(var t in this.events)this.unbind(t)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(t){var e=this.eventElements.filter(function(e){return e.element===t})[0];return"undefined"==typeof e&&(e=new r(t),this.eventElements.push(e)),e},o.prototype.bind=function(t,e,n){this.eventElement(t).bind(e,n)},o.prototype.unbind=function(t,e,n){this.eventElement(t).unbind(e,n)},o.prototype.unbindAll=function(){for(var t=0;te.scrollbarYTop?1:-1;i(t,"top",t.scrollTop+s*e.containerHeight),l(t),r.stopPropagation()}),e.event.bind(e.scrollbarX,"click",r),e.event.bind(e.scrollbarXRail,"click",function(r){var o=r.pageX-window.pageXOffset-n(e.scrollbarXRail).left,s=o>e.scrollbarXLeft?1:-1;i(t,"left",t.scrollLeft+s*e.containerWidth),l(t),r.stopPropagation()})}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(t,e,n){"use strict";function r(t,e){function n(n){var o=r+n*e.railXRatio,i=Math.max(0,e.scrollbarXRail.getBoundingClientRect().left)+e.railXRatio*(e.railXWidth-e.scrollbarXWidth);o<0?e.scrollbarXLeft=0:o>i?e.scrollbarXLeft=i:e.scrollbarXLeft=o;var s=l.toInt(e.scrollbarXLeft*(e.contentWidth-e.containerWidth)/(e.containerWidth-e.railXRatio*e.scrollbarXWidth))-e.negativeScrollAdjustment;c(t,"left",s)}var r=null,o=null,s=function(e){n(e.pageX-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"x"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarX,"mousedown",function(n){o=n.pageX,r=l.toInt(i.css(e.scrollbarX,"left"))*e.railXRatio,l.startScrolling(t,"x"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}function o(t,e){function n(n){var o=r+n*e.railYRatio,i=Math.max(0,e.scrollbarYRail.getBoundingClientRect().top)+e.railYRatio*(e.railYHeight-e.scrollbarYHeight);o<0?e.scrollbarYTop=0:o>i?e.scrollbarYTop=i:e.scrollbarYTop=o;var s=l.toInt(e.scrollbarYTop*(e.contentHeight-e.containerHeight)/(e.containerHeight-e.railYRatio*e.scrollbarYHeight));c(t,"top",s)}var r=null,o=null,s=function(e){n(e.pageY-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"y"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarY,"mousedown",function(n){o=n.pageY,r=l.toInt(i.css(e.scrollbarY,"top"))*e.railYRatio,l.startScrolling(t,"y"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}var l=t("../../lib/helper"),i=t("../../lib/dom"),s=t("../instances"),a=t("../update-geometry"),c=t("../update-scroll");e.exports=function(t){var e=s.get(t);r(t,e),o(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}var r=!1;e.event.bind(t,"mouseenter",function(){r=!0}),e.event.bind(t,"mouseleave",function(){r=!1});var i=!1;e.event.bind(e.ownerDocument,"keydown",function(c){if(!(c.isDefaultPrevented&&c.isDefaultPrevented()||c.defaultPrevented)){var u=l.matches(e.scrollbarX,":focus")||l.matches(e.scrollbarY,":focus");if(r||u){var d=document.activeElement?document.activeElement:e.ownerDocument.activeElement;if(d){if("IFRAME"===d.tagName)d=d.contentDocument.activeElement;else for(;d.shadowRoot;)d=d.shadowRoot.activeElement;if(o.isEditable(d))return}var p=0,f=0;switch(c.which){case 37:p=c.metaKey?-e.contentWidth:c.altKey?-e.containerWidth:-30;break;case 38:f=c.metaKey?e.contentHeight:c.altKey?e.containerHeight:30;break;case 39:p=c.metaKey?e.contentWidth:c.altKey?e.containerWidth:30;break;case 40:f=c.metaKey?-e.contentHeight:c.altKey?-e.containerHeight:-30;break;case 33:f=90;break;case 32:f=c.shiftKey?90:-90;break;case 34:f=-90;break;case 35:f=c.ctrlKey?-e.contentHeight:-e.containerHeight;break;case 36:f=c.ctrlKey?t.scrollTop:e.containerHeight;break;default:return}a(t,"top",t.scrollTop-f),a(t,"left",t.scrollLeft+p),s(t),i=n(p,f),i&&c.preventDefault()}}})}var o=t("../../lib/helper"),l=t("../../lib/dom"),i=t("../instances"),s=t("../update-geometry"),a=t("../update-scroll");e.exports=function(t){var e=i.get(t);r(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}function r(t){var e=t.deltaX,n=-1*t.deltaY;return"undefined"!=typeof e&&"undefined"!=typeof n||(e=-1*t.wheelDeltaX/6,n=t.wheelDeltaY/6),t.deltaMode&&1===t.deltaMode&&(e*=10,n*=10),e!==e&&n!==n&&(e=0,n=t.wheelDelta),t.shiftKey?[-n,-e]:[e,n]}function o(e,n){var r=t.querySelector("textarea:hover, select[multiple]:hover, .ps-child:hover");if(r){if(!window.getComputedStyle(r).overflow.match(/(scroll|auto)/))return!1;var o=r.scrollHeight-r.clientHeight;if(o>0&&!(0===r.scrollTop&&n>0||r.scrollTop===o&&n<0))return!0;var l=r.scrollLeft-r.clientWidth;if(l>0&&!(0===r.scrollLeft&&e<0||r.scrollLeft===l&&e>0))return!0}return!1}function s(s){var c=r(s),u=c[0],d=c[1];o(u,d)||(a=!1,e.settings.useBothWheelAxes?e.scrollbarYActive&&!e.scrollbarXActive?(d?i(t,"top",t.scrollTop-d*e.settings.wheelSpeed):i(t,"top",t.scrollTop+u*e.settings.wheelSpeed),a=!0):e.scrollbarXActive&&!e.scrollbarYActive&&(u?i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed):i(t,"left",t.scrollLeft-d*e.settings.wheelSpeed),a=!0):(i(t,"top",t.scrollTop-d*e.settings.wheelSpeed),i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed)),l(t),a=a||n(u,d),a&&(s.stopPropagation(),s.preventDefault()))}var a=!1;"undefined"!=typeof window.onwheel?e.event.bind(t,"wheel",s):"undefined"!=typeof window.onmousewheel&&e.event.bind(t,"mousewheel",s)}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(t,e,n){"use strict";function r(t,e){e.event.bind(t,"scroll",function(){l(t)})}var o=t("../instances"),l=t("../update-geometry");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19}],15:[function(t,e,n){"use strict";function r(t,e){function n(){var t=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===t.toString().length?null:t.getRangeAt(0).commonAncestorContainer}function r(){c||(c=setInterval(function(){return l.get(t)?(s(t,"top",t.scrollTop+u.top),s(t,"left",t.scrollLeft+u.left),void i(t)):void clearInterval(c)},50))}function a(){c&&(clearInterval(c),c=null),o.stopScrolling(t)}var c=null,u={top:0,left:0},d=!1;e.event.bind(e.ownerDocument,"selectionchange",function(){t.contains(n())?d=!0:(d=!1,a())}),e.event.bind(window,"mouseup",function(){d&&(d=!1,a())}),e.event.bind(window,"keyup",function(){d&&(d=!1,a())}),e.event.bind(window,"mousemove",function(e){if(d){var n={x:e.pageX,y:e.pageY},l={left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,top:t.offsetTop,bottom:t.offsetTop+t.offsetHeight};n.xl.right-3?(u.left=5,o.startScrolling(t,"x")):u.left=0,n.yl.bottom-3?(n.y-l.bottom+3<5?u.top=5:u.top=20,o.startScrolling(t,"y")):u.top=0,0===u.top&&0===u.left?a():r()}})}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){var e=l.get(t);r(t,e)}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(t,e,n){"use strict";function r(t,e,n,r){function o(n,r){var o=t.scrollTop,l=t.scrollLeft,i=Math.abs(n),s=Math.abs(r);if(s>i){if(r<0&&o===e.contentHeight-e.containerHeight||r>0&&0===o)return!e.settings.swipePropagation}else if(i>s&&(n<0&&l===e.contentWidth-e.containerWidth||n>0&&0===l))return!e.settings.swipePropagation;return!0}function a(e,n){s(t,"top",t.scrollTop-n),s(t,"left",t.scrollLeft-e),i(t)}function c(){w=!0}function u(){w=!1}function d(t){return t.targetTouches?t.targetTouches[0]:t}function p(t){return!(!t.targetTouches||1!==t.targetTouches.length)||!(!t.pointerType||"mouse"===t.pointerType||t.pointerType===t.MSPOINTER_TYPE_MOUSE)}function f(t){if(p(t)){Y=!0;var e=d(t);g.pageX=e.pageX,g.pageY=e.pageY,v=(new Date).getTime(),null!==y&&clearInterval(y),t.stopPropagation()}}function h(t){if(!Y&&e.settings.swipePropagation&&f(t),!w&&Y&&p(t)){var n=d(t),r={pageX:n.pageX,pageY:n.pageY},l=r.pageX-g.pageX,i=r.pageY-g.pageY;a(l,i),g=r;var s=(new Date).getTime(),c=s-v;c>0&&(m.x=l/c,m.y=i/c,v=s),o(l,i)&&(t.stopPropagation(),t.preventDefault())}}function b(){!w&&Y&&(Y=!1,clearInterval(y),y=setInterval(function(){return l.get(t)&&(m.x||m.y)?Math.abs(m.x)<.01&&Math.abs(m.y)<.01?void clearInterval(y):(a(30*m.x,30*m.y),m.x*=.8,void(m.y*=.8)):void clearInterval(y)},10))}var g={},v=0,m={},y=null,w=!1,Y=!1;n?(e.event.bind(window,"touchstart",c),e.event.bind(window,"touchend",u),e.event.bind(t,"touchstart",f),e.event.bind(t,"touchmove",h),e.event.bind(t,"touchend",b)):r&&(window.PointerEvent?(e.event.bind(window,"pointerdown",c),e.event.bind(window,"pointerup",u),e.event.bind(t,"pointerdown",f),e.event.bind(t,"pointermove",h),e.event.bind(t,"pointerup",b)):window.MSPointerEvent&&(e.event.bind(window,"MSPointerDown",c),e.event.bind(window,"MSPointerUp",u),e.event.bind(t,"MSPointerDown",f),e.event.bind(t,"MSPointerMove",h),e.event.bind(t,"MSPointerUp",b)))}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){if(o.env.supportsTouch||o.env.supportsIePointer){var e=l.get(t);r(t,e,o.env.supportsTouch,o.env.supportsIePointer)}}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/class"),l=t("./instances"),i=t("./update-geometry"),s={"click-rail":t("./handler/click-rail"),"drag-scrollbar":t("./handler/drag-scrollbar"),keyboard:t("./handler/keyboard"),wheel:t("./handler/mouse-wheel"),touch:t("./handler/touch"),selection:t("./handler/selection")},a=t("./handler/native-scroll");e.exports=function(t,e){e="object"==typeof e?e:{},o.add(t,"ps-container");var n=l.add(t);n.settings=r.extend(n.settings,e),o.add(t,"ps-theme-"+n.settings.theme),n.settings.handlers.forEach(function(e){s[e](t)}),a(t),i(t)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(t,e,n){"use strict";function r(t){function e(){a.add(t,"ps-focus")}function n(){a.remove(t,"ps-focus")}var r=this;r.settings=s.clone(c),r.containerWidth=null,r.containerHeight=null,r.contentWidth=null,r.contentHeight=null,r.isRtl="rtl"===u.css(t,"direction"),r.isNegativeScroll=function(){var e=t.scrollLeft,n=null;return t.scrollLeft=-1,n=t.scrollLeft<0,t.scrollLeft=e,n}(),r.negativeScrollAdjustment=r.isNegativeScroll?t.scrollWidth-t.clientWidth:0,r.event=new d,r.ownerDocument=t.ownerDocument||document,r.scrollbarXRail=u.appendTo(u.e("div","ps-scrollbar-x-rail"),t),r.scrollbarX=u.appendTo(u.e("div","ps-scrollbar-x"),r.scrollbarXRail),r.scrollbarX.setAttribute("tabindex",0),r.event.bind(r.scrollbarX,"focus",e),r.event.bind(r.scrollbarX,"blur",n),r.scrollbarXActive=null,r.scrollbarXWidth=null,r.scrollbarXLeft=null,r.scrollbarXBottom=s.toInt(u.css(r.scrollbarXRail,"bottom")),r.isScrollbarXUsingBottom=r.scrollbarXBottom===r.scrollbarXBottom,r.scrollbarXTop=r.isScrollbarXUsingBottom?null:s.toInt(u.css(r.scrollbarXRail,"top")),r.railBorderXWidth=s.toInt(u.css(r.scrollbarXRail,"borderLeftWidth"))+s.toInt(u.css(r.scrollbarXRail,"borderRightWidth")),u.css(r.scrollbarXRail,"display","block"),r.railXMarginWidth=s.toInt(u.css(r.scrollbarXRail,"marginLeft"))+s.toInt(u.css(r.scrollbarXRail,"marginRight")),u.css(r.scrollbarXRail,"display",""),r.railXWidth=null,r.railXRatio=null,r.scrollbarYRail=u.appendTo(u.e("div","ps-scrollbar-y-rail"),t),r.scrollbarY=u.appendTo(u.e("div","ps-scrollbar-y"),r.scrollbarYRail),r.scrollbarY.setAttribute("tabindex",0),r.event.bind(r.scrollbarY,"focus",e),r.event.bind(r.scrollbarY,"blur",n),r.scrollbarYActive=null,r.scrollbarYHeight=null,r.scrollbarYTop=null,r.scrollbarYRight=s.toInt(u.css(r.scrollbarYRail,"right")),r.isScrollbarYUsingRight=r.scrollbarYRight===r.scrollbarYRight,r.scrollbarYLeft=r.isScrollbarYUsingRight?null:s.toInt(u.css(r.scrollbarYRail,"left")),r.scrollbarYOuterWidth=r.isRtl?s.outerWidth(r.scrollbarY):null,r.railBorderYWidth=s.toInt(u.css(r.scrollbarYRail,"borderTopWidth"))+s.toInt(u.css(r.scrollbarYRail,"borderBottomWidth")),u.css(r.scrollbarYRail,"display","block"),r.railYMarginHeight=s.toInt(u.css(r.scrollbarYRail,"marginTop"))+s.toInt(u.css(r.scrollbarYRail,"marginBottom")),u.css(r.scrollbarYRail,"display",""),r.railYHeight=null,r.railYRatio=null}function o(t){return t.getAttribute("data-ps-id")}function l(t,e){t.setAttribute("data-ps-id",e)}function i(t){t.removeAttribute("data-ps-id")}var s=t("../lib/helper"),a=t("../lib/class"),c=t("./default-setting"),u=t("../lib/dom"),d=t("../lib/event-manager"),p=t("../lib/guid"),f={};n.add=function(t){var e=p();return l(t,e),f[e]=new r(t),f[e]},n.remove=function(t){delete f[o(t)],i(t)},n.get=function(t){return f[o(t)]}},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(t,e,n){"use strict";function r(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.settings.minScrollbarLength)),t.settings.maxScrollbarLength&&(e=Math.min(e,t.settings.maxScrollbarLength)),e}function o(t,e){var n={width:e.railXWidth};e.isRtl?n.left=e.negativeScrollAdjustment+t.scrollLeft+e.containerWidth-e.contentWidth:n.left=t.scrollLeft,e.isScrollbarXUsingBottom?n.bottom=e.scrollbarXBottom-t.scrollTop:n.top=e.scrollbarXTop+t.scrollTop,s.css(e.scrollbarXRail,n);var r={top:t.scrollTop,height:e.railYHeight};e.isScrollbarYUsingRight?e.isRtl?r.right=e.contentWidth-(e.negativeScrollAdjustment+t.scrollLeft)-e.scrollbarYRight-e.scrollbarYOuterWidth:r.right=e.scrollbarYRight-t.scrollLeft:e.isRtl?r.left=e.negativeScrollAdjustment+t.scrollLeft+2*e.containerWidth-e.contentWidth-e.scrollbarYLeft-e.scrollbarYOuterWidth:r.left=e.scrollbarYLeft+t.scrollLeft,s.css(e.scrollbarYRail,r),s.css(e.scrollbarX,{left:e.scrollbarXLeft,width:e.scrollbarXWidth-e.railBorderXWidth}),s.css(e.scrollbarY,{top:e.scrollbarYTop,height:e.scrollbarYHeight-e.railBorderYWidth})}var l=t("../lib/helper"),i=t("../lib/class"),s=t("../lib/dom"),a=t("./instances"),c=t("./update-scroll");e.exports=function(t){var e=a.get(t);e.containerWidth=t.clientWidth,e.containerHeight=t.clientHeight,e.contentWidth=t.scrollWidth,e.contentHeight=t.scrollHeight;var n;t.contains(e.scrollbarXRail)||(n=s.queryChildren(t,".ps-scrollbar-x-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarXRail,t)),t.contains(e.scrollbarYRail)||(n=s.queryChildren(t,".ps-scrollbar-y-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarYRail,t)),!e.settings.suppressScrollX&&e.containerWidth+e.settings.scrollXMarginOffset=e.railXWidth-e.scrollbarXWidth&&(e.scrollbarXLeft=e.railXWidth-e.scrollbarXWidth),e.scrollbarYTop>=e.railYHeight-e.scrollbarYHeight&&(e.scrollbarYTop=e.railYHeight-e.scrollbarYHeight),o(t,e),e.scrollbarXActive?i.add(t,"ps-active-x"):(i.remove(t,"ps-active-x"),e.scrollbarXWidth=0,e.scrollbarXLeft=0,c(t,"left",0)),e.scrollbarYActive?i.add(t,"ps-active-y"):(i.remove(t,"ps-active-y"),e.scrollbarYHeight=0,e.scrollbarYTop=0,c(t,"top",0))}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(t,e,n){"use strict";var r,o,l=t("./instances"),i=function(t){var e=document.createEvent("Event");return e.initEvent(t,!0,!0),e};e.exports=function(t,e,n){if("undefined"==typeof t)throw"You must provide an element to the update-scroll function";if("undefined"==typeof e)throw"You must provide an axis to the update-scroll function";if("undefined"==typeof n)throw"You must provide a value to the update-scroll function";"top"===e&&n<=0&&(t.scrollTop=n=0,t.dispatchEvent(i("ps-y-reach-start"))),"left"===e&&n<=0&&(t.scrollLeft=n=0,t.dispatchEvent(i("ps-x-reach-start")));var s=l.get(t);"top"===e&&n>=s.contentHeight-s.containerHeight&&(n=s.contentHeight-s.containerHeight,n-t.scrollTop<=1?n=t.scrollTop:t.scrollTop=n,t.dispatchEvent(i("ps-y-reach-end"))),"left"===e&&n>=s.contentWidth-s.containerWidth&&(n=s.contentWidth-s.containerWidth,n-t.scrollLeft<=1?n=t.scrollLeft:t.scrollLeft=n,t.dispatchEvent(i("ps-x-reach-end"))),r||(r=t.scrollTop),o||(o=t.scrollLeft),"top"===e&&nr&&t.dispatchEvent(i("ps-scroll-down")),"left"===e&&no&&t.dispatchEvent(i("ps-scroll-right")),"top"===e&&(t.scrollTop=r=n,t.dispatchEvent(i("ps-scroll-y"))),"left"===e&&(t.scrollLeft=o=n,t.dispatchEvent(i("ps-scroll-x")))}},{"./instances":18}],21:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances"),i=t("./update-geometry"),s=t("./update-scroll");e.exports=function(t){var e=l.get(t);e&&(e.negativeScrollAdjustment=e.isNegativeScroll?t.scrollWidth-t.clientWidth:0,o.css(e.scrollbarXRail,"display","block"),o.css(e.scrollbarYRail,"display","block"),e.railXMarginWidth=r.toInt(o.css(e.scrollbarXRail,"marginLeft"))+r.toInt(o.css(e.scrollbarXRail,"marginRight")),e.railYMarginHeight=r.toInt(o.css(e.scrollbarYRail,"marginTop"))+r.toInt(o.css(e.scrollbarYRail,"marginBottom")),o.css(e.scrollbarXRail,"display","none"),o.css(e.scrollbarYRail,"display","none"),i(t),s(t,"top",t.scrollTop),s(t,"left",t.scrollLeft),o.css(e.scrollbarXRail,"display",""),o.css(e.scrollbarYRail,"display",""))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); \ No newline at end of file diff --git a/view/js/perfect-scrollbar/js/perfect-scrollbar.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.js new file mode 100644 index 000000000..ad0e71f3d --- /dev/null +++ b/view/js/perfect-scrollbar/js/perfect-scrollbar.js @@ -0,0 +1,1550 @@ +/* perfect-scrollbar v0.6.16 */ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { + classes.splice(idx, 1); + } + element.className = classes.join(' '); +} + +exports.add = function (element, className) { + if (element.classList) { + element.classList.add(className); + } else { + oldAdd(element, className); + } +}; + +exports.remove = function (element, className) { + if (element.classList) { + element.classList.remove(className); + } else { + oldRemove(element, className); + } +}; + +exports.list = function (element) { + if (element.classList) { + return Array.prototype.slice.apply(element.classList); + } else { + return element.className.split(' '); + } +}; + +},{}],3:[function(require,module,exports){ +'use strict'; + +var DOM = {}; + +DOM.e = function (tagName, className) { + var element = document.createElement(tagName); + element.className = className; + return element; +}; + +DOM.appendTo = function (child, parent) { + parent.appendChild(child); + return child; +}; + +function cssGet(element, styleName) { + return window.getComputedStyle(element)[styleName]; +} + +function cssSet(element, styleName, styleValue) { + if (typeof styleValue === 'number') { + styleValue = styleValue.toString() + 'px'; + } + element.style[styleName] = styleValue; + return element; +} + +function cssMultiSet(element, obj) { + for (var key in obj) { + var val = obj[key]; + if (typeof val === 'number') { + val = val.toString() + 'px'; + } + element.style[key] = val; + } + return element; +} + +DOM.css = function (element, styleNameOrObject, styleValue) { + if (typeof styleNameOrObject === 'object') { + // multiple set with object + return cssMultiSet(element, styleNameOrObject); + } else { + if (typeof styleValue === 'undefined') { + return cssGet(element, styleNameOrObject); + } else { + return cssSet(element, styleNameOrObject, styleValue); + } + } +}; + +DOM.matches = function (element, query) { + if (typeof element.matches !== 'undefined') { + return element.matches(query); + } else { + if (typeof element.matchesSelector !== 'undefined') { + return element.matchesSelector(query); + } else if (typeof element.webkitMatchesSelector !== 'undefined') { + return element.webkitMatchesSelector(query); + } else if (typeof element.mozMatchesSelector !== 'undefined') { + return element.mozMatchesSelector(query); + } else if (typeof element.msMatchesSelector !== 'undefined') { + return element.msMatchesSelector(query); + } + } +}; + +DOM.remove = function (element) { + if (typeof element.remove !== 'undefined') { + element.remove(); + } else { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + } +}; + +DOM.queryChildren = function (element, selector) { + return Array.prototype.filter.call(element.childNodes, function (child) { + return DOM.matches(child, selector); + }); +}; + +module.exports = DOM; + +},{}],4:[function(require,module,exports){ +'use strict'; + +var EventElement = function (element) { + this.element = element; + this.events = {}; +}; + +EventElement.prototype.bind = function (eventName, handler) { + if (typeof this.events[eventName] === 'undefined') { + this.events[eventName] = []; + } + this.events[eventName].push(handler); + this.element.addEventListener(eventName, handler, false); +}; + +EventElement.prototype.unbind = function (eventName, handler) { + var isHandlerProvided = (typeof handler !== 'undefined'); + this.events[eventName] = this.events[eventName].filter(function (hdlr) { + if (isHandlerProvided && hdlr !== handler) { + return true; + } + this.element.removeEventListener(eventName, hdlr, false); + return false; + }, this); +}; + +EventElement.prototype.unbindAll = function () { + for (var name in this.events) { + this.unbind(name); + } +}; + +var EventManager = function () { + this.eventElements = []; +}; + +EventManager.prototype.eventElement = function (element) { + var ee = this.eventElements.filter(function (eventElement) { + return eventElement.element === element; + })[0]; + if (typeof ee === 'undefined') { + ee = new EventElement(element); + this.eventElements.push(ee); + } + return ee; +}; + +EventManager.prototype.bind = function (element, eventName, handler) { + this.eventElement(element).bind(eventName, handler); +}; + +EventManager.prototype.unbind = function (element, eventName, handler) { + this.eventElement(element).unbind(eventName, handler); +}; + +EventManager.prototype.unbindAll = function () { + for (var i = 0; i < this.eventElements.length; i++) { + this.eventElements[i].unbindAll(); + } +}; + +EventManager.prototype.once = function (element, eventName, handler) { + var ee = this.eventElement(element); + var onceHandler = function (e) { + ee.unbind(eventName, onceHandler); + handler(e); + }; + ee.bind(eventName, onceHandler); +}; + +module.exports = EventManager; + +},{}],5:[function(require,module,exports){ +'use strict'; + +module.exports = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; +})(); + +},{}],6:[function(require,module,exports){ +'use strict'; + +var cls = require('./class'); +var dom = require('./dom'); + +var toInt = exports.toInt = function (x) { + return parseInt(x, 10) || 0; +}; + +var clone = exports.clone = function (obj) { + if (!obj) { + return null; + } else if (obj.constructor === Array) { + return obj.map(clone); + } else if (typeof obj === 'object') { + var result = {}; + for (var key in obj) { + result[key] = clone(obj[key]); + } + return result; + } else { + return obj; + } +}; + +exports.extend = function (original, source) { + var result = clone(original); + for (var key in source) { + result[key] = clone(source[key]); + } + return result; +}; + +exports.isEditable = function (el) { + return dom.matches(el, "input,[contenteditable]") || + dom.matches(el, "select,[contenteditable]") || + dom.matches(el, "textarea,[contenteditable]") || + dom.matches(el, "button,[contenteditable]"); +}; + +exports.removePsClasses = function (element) { + var clsList = cls.list(element); + for (var i = 0; i < clsList.length; i++) { + var className = clsList[i]; + if (className.indexOf('ps-') === 0) { + cls.remove(element, className); + } + } +}; + +exports.outerWidth = function (element) { + return toInt(dom.css(element, 'width')) + + toInt(dom.css(element, 'paddingLeft')) + + toInt(dom.css(element, 'paddingRight')) + + toInt(dom.css(element, 'borderLeftWidth')) + + toInt(dom.css(element, 'borderRightWidth')); +}; + +exports.startScrolling = function (element, axis) { + cls.add(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.add(element, 'ps-' + axis); + } else { + cls.add(element, 'ps-x'); + cls.add(element, 'ps-y'); + } +}; + +exports.stopScrolling = function (element, axis) { + cls.remove(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.remove(element, 'ps-' + axis); + } else { + cls.remove(element, 'ps-x'); + cls.remove(element, 'ps-y'); + } +}; + +exports.env = { + isWebKit: 'WebkitAppearance' in document.documentElement.style, + supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + supportsIePointer: window.navigator.msMaxTouchPoints !== null +}; + +},{"./class":2,"./dom":3}],7:[function(require,module,exports){ +'use strict'; + +var destroy = require('./plugin/destroy'); +var initialize = require('./plugin/initialize'); +var update = require('./plugin/update'); + +module.exports = { + initialize: initialize, + update: update, + destroy: destroy +}; + +},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(require,module,exports){ +'use strict'; + +module.exports = { + handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'], + maxScrollbarLength: null, + minScrollbarLength: null, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + suppressScrollX: false, + suppressScrollY: false, + swipePropagation: true, + useBothWheelAxes: false, + wheelPropagation: false, + wheelSpeed: 1, + theme: 'default' +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + i.event.unbindAll(); + dom.remove(i.scrollbarX); + dom.remove(i.scrollbarY); + dom.remove(i.scrollbarXRail); + dom.remove(i.scrollbarYRail); + _.removePsClasses(element); + + instances.remove(element); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindClickRailHandler(element, i) { + function pageOffset(el) { + return el.getBoundingClientRect(); + } + var stopPropagation = function (e) { e.stopPropagation(); }; + + i.event.bind(i.scrollbarY, 'click', stopPropagation); + i.event.bind(i.scrollbarYRail, 'click', function (e) { + var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top; + var direction = positionTop > i.scrollbarYTop ? 1 : -1; + + updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight); + updateGeometry(element); + + e.stopPropagation(); + }); + + i.event.bind(i.scrollbarX, 'click', stopPropagation); + i.event.bind(i.scrollbarXRail, 'click', function (e) { + var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left; + var direction = positionLeft > i.scrollbarXLeft ? 1 : -1; + + updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth); + updateGeometry(element); + + e.stopPropagation(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindClickRailHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseScrollXHandler(element, i) { + var currentLeft = null; + var currentPageX = null; + + function updateScrollLeft(deltaX) { + var newLeft = currentLeft + (deltaX * i.railXRatio); + var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth)); + + if (newLeft < 0) { + i.scrollbarXLeft = 0; + } else if (newLeft > maxLeft) { + i.scrollbarXLeft = maxLeft; + } else { + i.scrollbarXLeft = newLeft; + } + + var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment; + updateScroll(element, 'left', scrollLeft); + } + + var mouseMoveHandler = function (e) { + updateScrollLeft(e.pageX - currentPageX); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'x'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarX, 'mousedown', function (e) { + currentPageX = e.pageX; + currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio; + _.startScrolling(element, 'x'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +function bindMouseScrollYHandler(element, i) { + var currentTop = null; + var currentPageY = null; + + function updateScrollTop(deltaY) { + var newTop = currentTop + (deltaY * i.railYRatio); + var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight)); + + if (newTop < 0) { + i.scrollbarYTop = 0; + } else if (newTop > maxTop) { + i.scrollbarYTop = maxTop; + } else { + i.scrollbarYTop = newTop; + } + + var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight))); + updateScroll(element, 'top', scrollTop); + } + + var mouseMoveHandler = function (e) { + updateScrollTop(e.pageY - currentPageY); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'y'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarY, 'mousedown', function (e) { + currentPageY = e.pageY; + currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio; + _.startScrolling(element, 'y'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseScrollXHandler(element, i); + bindMouseScrollYHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindKeyboardHandler(element, i) { + var hovered = false; + i.event.bind(element, 'mouseenter', function () { + hovered = true; + }); + i.event.bind(element, 'mouseleave', function () { + hovered = false; + }); + + var shouldPrevent = false; + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + i.event.bind(i.ownerDocument, 'keydown', function (e) { + if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) { + return; + } + + var focused = dom.matches(i.scrollbarX, ':focus') || + dom.matches(i.scrollbarY, ':focus'); + + if (!hovered && !focused) { + return; + } + + var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement; + if (activeElement) { + if (activeElement.tagName === 'IFRAME') { + activeElement = activeElement.contentDocument.activeElement; + } else { + // go deeper if element is a webcomponent + while (activeElement.shadowRoot) { + activeElement = activeElement.shadowRoot.activeElement; + } + } + if (_.isEditable(activeElement)) { + return; + } + } + + var deltaX = 0; + var deltaY = 0; + + switch (e.which) { + case 37: // left + if (e.metaKey) { + deltaX = -i.contentWidth; + } else if (e.altKey) { + deltaX = -i.containerWidth; + } else { + deltaX = -30; + } + break; + case 38: // up + if (e.metaKey) { + deltaY = i.contentHeight; + } else if (e.altKey) { + deltaY = i.containerHeight; + } else { + deltaY = 30; + } + break; + case 39: // right + if (e.metaKey) { + deltaX = i.contentWidth; + } else if (e.altKey) { + deltaX = i.containerWidth; + } else { + deltaX = 30; + } + break; + case 40: // down + if (e.metaKey) { + deltaY = -i.contentHeight; + } else if (e.altKey) { + deltaY = -i.containerHeight; + } else { + deltaY = -30; + } + break; + case 33: // page up + deltaY = 90; + break; + case 32: // space bar + if (e.shiftKey) { + deltaY = 90; + } else { + deltaY = -90; + } + break; + case 34: // page down + deltaY = -90; + break; + case 35: // end + if (e.ctrlKey) { + deltaY = -i.contentHeight; + } else { + deltaY = -i.containerHeight; + } + break; + case 36: // home + if (e.ctrlKey) { + deltaY = element.scrollTop; + } else { + deltaY = i.containerHeight; + } + break; + default: + return; + } + + updateScroll(element, 'top', element.scrollTop - deltaY); + updateScroll(element, 'left', element.scrollLeft + deltaX); + updateGeometry(element); + + shouldPrevent = shouldPreventDefault(deltaX, deltaY); + if (shouldPrevent) { + e.preventDefault(); + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindKeyboardHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseWheelHandler(element, i) { + var shouldPrevent = false; + + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + function getDeltaFromEvent(e) { + var deltaX = e.deltaX; + var deltaY = -1 * e.deltaY; + + if (typeof deltaX === "undefined" || typeof deltaY === "undefined") { + // OS X Safari + deltaX = -1 * e.wheelDeltaX / 6; + deltaY = e.wheelDeltaY / 6; + } + + if (e.deltaMode && e.deltaMode === 1) { + // Firefox in deltaMode 1: Line scrolling + deltaX *= 10; + deltaY *= 10; + } + + if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) { + // IE in some mouse drivers + deltaX = 0; + deltaY = e.wheelDelta; + } + + if (e.shiftKey) { + // reverse axis with shift key + return [-deltaY, -deltaX]; + } + return [deltaX, deltaY]; + } + + function shouldBeConsumedByChild(deltaX, deltaY) { + var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover'); + if (child) { + if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) { + // if not scrollable + return false; + } + + var maxScrollTop = child.scrollHeight - child.clientHeight; + if (maxScrollTop > 0) { + if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) { + return true; + } + } + var maxScrollLeft = child.scrollLeft - child.clientWidth; + if (maxScrollLeft > 0) { + if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) { + return true; + } + } + } + return false; + } + + function mousewheelHandler(e) { + var delta = getDeltaFromEvent(e); + + var deltaX = delta[0]; + var deltaY = delta[1]; + + if (shouldBeConsumedByChild(deltaX, deltaY)) { + return; + } + + shouldPrevent = false; + if (!i.settings.useBothWheelAxes) { + // deltaX will only be used for horizontal scrolling and deltaY will + // only be used for vertical scrolling - this is the default + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else if (i.scrollbarYActive && !i.scrollbarXActive) { + // only vertical scrollbar is active and useBothWheelAxes option is + // active, so let's scroll vertical bar using both mouse wheel axes + if (deltaY) { + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } else if (i.scrollbarXActive && !i.scrollbarYActive) { + // useBothWheelAxes and only horizontal bar is active, so use both + // wheel axes for horizontal bar + if (deltaX) { + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } + + updateGeometry(element); + + shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY)); + if (shouldPrevent) { + e.stopPropagation(); + e.preventDefault(); + } + } + + if (typeof window.onwheel !== "undefined") { + i.event.bind(element, 'wheel', mousewheelHandler); + } else if (typeof window.onmousewheel !== "undefined") { + i.event.bind(element, 'mousewheel', mousewheelHandler); + } +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseWheelHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); + +function bindNativeScrollHandler(element, i) { + i.event.bind(element, 'scroll', function () { + updateGeometry(element); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindNativeScrollHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19}],15:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindSelectionHandler(element, i) { + function getRangeNode() { + var selection = window.getSelection ? window.getSelection() : + document.getSelection ? document.getSelection() : ''; + if (selection.toString().length === 0) { + return null; + } else { + return selection.getRangeAt(0).commonAncestorContainer; + } + } + + var scrollingLoop = null; + var scrollDiff = {top: 0, left: 0}; + function startScrolling() { + if (!scrollingLoop) { + scrollingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(scrollingLoop); + return; + } + + updateScroll(element, 'top', element.scrollTop + scrollDiff.top); + updateScroll(element, 'left', element.scrollLeft + scrollDiff.left); + updateGeometry(element); + }, 50); // every .1 sec + } + } + function stopScrolling() { + if (scrollingLoop) { + clearInterval(scrollingLoop); + scrollingLoop = null; + } + _.stopScrolling(element); + } + + var isSelected = false; + i.event.bind(i.ownerDocument, 'selectionchange', function () { + if (element.contains(getRangeNode())) { + isSelected = true; + } else { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'mouseup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'keyup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + + i.event.bind(window, 'mousemove', function (e) { + if (isSelected) { + var mousePosition = {x: e.pageX, y: e.pageY}; + var containerGeometry = { + left: element.offsetLeft, + right: element.offsetLeft + element.offsetWidth, + top: element.offsetTop, + bottom: element.offsetTop + element.offsetHeight + }; + + if (mousePosition.x < containerGeometry.left + 3) { + scrollDiff.left = -5; + _.startScrolling(element, 'x'); + } else if (mousePosition.x > containerGeometry.right - 3) { + scrollDiff.left = 5; + _.startScrolling(element, 'x'); + } else { + scrollDiff.left = 0; + } + + if (mousePosition.y < containerGeometry.top + 3) { + if (containerGeometry.top + 3 - mousePosition.y < 5) { + scrollDiff.top = -5; + } else { + scrollDiff.top = -20; + } + _.startScrolling(element, 'y'); + } else if (mousePosition.y > containerGeometry.bottom - 3) { + if (mousePosition.y - containerGeometry.bottom + 3 < 5) { + scrollDiff.top = 5; + } else { + scrollDiff.top = 20; + } + _.startScrolling(element, 'y'); + } else { + scrollDiff.top = 0; + } + + if (scrollDiff.top === 0 && scrollDiff.left === 0) { + stopScrolling(); + } else { + startScrolling(); + } + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindSelectionHandler(element, i); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindTouchHandler(element, i, supportsTouch, supportsIePointer) { + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + var scrollLeft = element.scrollLeft; + var magnitudeX = Math.abs(deltaX); + var magnitudeY = Math.abs(deltaY); + + if (magnitudeY > magnitudeX) { + // user is perhaps trying to swipe up/down the page + + if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) || + ((deltaY > 0) && (scrollTop === 0))) { + return !i.settings.swipePropagation; + } + } else if (magnitudeX > magnitudeY) { + // user is perhaps trying to swipe left/right across the page + + if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) || + ((deltaX > 0) && (scrollLeft === 0))) { + return !i.settings.swipePropagation; + } + } + + return true; + } + + function applyTouchMove(differenceX, differenceY) { + updateScroll(element, 'top', element.scrollTop - differenceY); + updateScroll(element, 'left', element.scrollLeft - differenceX); + + updateGeometry(element); + } + + var startOffset = {}; + var startTime = 0; + var speed = {}; + var easingLoop = null; + var inGlobalTouch = false; + var inLocalTouch = false; + + function globalTouchStart() { + inGlobalTouch = true; + } + function globalTouchEnd() { + inGlobalTouch = false; + } + + function getTouch(e) { + if (e.targetTouches) { + return e.targetTouches[0]; + } else { + // Maybe IE pointer + return e; + } + } + function shouldHandle(e) { + if (e.targetTouches && e.targetTouches.length === 1) { + return true; + } + if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + return true; + } + return false; + } + function touchStart(e) { + if (shouldHandle(e)) { + inLocalTouch = true; + + var touch = getTouch(e); + + startOffset.pageX = touch.pageX; + startOffset.pageY = touch.pageY; + + startTime = (new Date()).getTime(); + + if (easingLoop !== null) { + clearInterval(easingLoop); + } + + e.stopPropagation(); + } + } + function touchMove(e) { + if (!inLocalTouch && i.settings.swipePropagation) { + touchStart(e); + } + if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) { + var touch = getTouch(e); + + var currentOffset = {pageX: touch.pageX, pageY: touch.pageY}; + + var differenceX = currentOffset.pageX - startOffset.pageX; + var differenceY = currentOffset.pageY - startOffset.pageY; + + applyTouchMove(differenceX, differenceY); + startOffset = currentOffset; + + var currentTime = (new Date()).getTime(); + + var timeGap = currentTime - startTime; + if (timeGap > 0) { + speed.x = differenceX / timeGap; + speed.y = differenceY / timeGap; + startTime = currentTime; + } + + if (shouldPreventDefault(differenceX, differenceY)) { + e.stopPropagation(); + e.preventDefault(); + } + } + } + function touchEnd() { + if (!inGlobalTouch && inLocalTouch) { + inLocalTouch = false; + + clearInterval(easingLoop); + easingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(easingLoop); + return; + } + + if (!speed.x && !speed.y) { + clearInterval(easingLoop); + return; + } + + if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(easingLoop); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + } + } + + if (supportsTouch) { + i.event.bind(window, 'touchstart', globalTouchStart); + i.event.bind(window, 'touchend', globalTouchEnd); + i.event.bind(element, 'touchstart', touchStart); + i.event.bind(element, 'touchmove', touchMove); + i.event.bind(element, 'touchend', touchEnd); + } else if (supportsIePointer) { + if (window.PointerEvent) { + i.event.bind(window, 'pointerdown', globalTouchStart); + i.event.bind(window, 'pointerup', globalTouchEnd); + i.event.bind(element, 'pointerdown', touchStart); + i.event.bind(element, 'pointermove', touchMove); + i.event.bind(element, 'pointerup', touchEnd); + } else if (window.MSPointerEvent) { + i.event.bind(window, 'MSPointerDown', globalTouchStart); + i.event.bind(window, 'MSPointerUp', globalTouchEnd); + i.event.bind(element, 'MSPointerDown', touchStart); + i.event.bind(element, 'MSPointerMove', touchMove); + i.event.bind(element, 'MSPointerUp', touchEnd); + } + } +} + +module.exports = function (element) { + if (!_.env.supportsTouch && !_.env.supportsIePointer) { + return; + } + + var i = instances.get(element); + bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); + +// Handlers +var handlers = { + 'click-rail': require('./handler/click-rail'), + 'drag-scrollbar': require('./handler/drag-scrollbar'), + 'keyboard': require('./handler/keyboard'), + 'wheel': require('./handler/mouse-wheel'), + 'touch': require('./handler/touch'), + 'selection': require('./handler/selection') +}; +var nativeScrollHandler = require('./handler/native-scroll'); + +module.exports = function (element, userSettings) { + userSettings = typeof userSettings === 'object' ? userSettings : {}; + + cls.add(element, 'ps-container'); + + // Create a plugin instance. + var i = instances.add(element); + + i.settings = _.extend(i.settings, userSettings); + cls.add(element, 'ps-theme-' + i.settings.theme); + + i.settings.handlers.forEach(function (handlerName) { + handlers[handlerName](element); + }); + + nativeScrollHandler(element); + + updateGeometry(element); +}; + +},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var defaultSettings = require('./default-setting'); +var dom = require('../lib/dom'); +var EventManager = require('../lib/event-manager'); +var guid = require('../lib/guid'); + +var instances = {}; + +function Instance(element) { + var i = this; + + i.settings = _.clone(defaultSettings); + i.containerWidth = null; + i.containerHeight = null; + i.contentWidth = null; + i.contentHeight = null; + + i.isRtl = dom.css(element, 'direction') === "rtl"; + i.isNegativeScroll = (function () { + var originalScrollLeft = element.scrollLeft; + var result = null; + element.scrollLeft = -1; + result = element.scrollLeft < 0; + element.scrollLeft = originalScrollLeft; + return result; + })(); + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + i.event = new EventManager(); + i.ownerDocument = element.ownerDocument || document; + + function focus() { + cls.add(element, 'ps-focus'); + } + + function blur() { + cls.remove(element, 'ps-focus'); + } + + i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element); + i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail); + i.scrollbarX.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarX, 'focus', focus); + i.event.bind(i.scrollbarX, 'blur', blur); + i.scrollbarXActive = null; + i.scrollbarXWidth = null; + i.scrollbarXLeft = null; + i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom')); + i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN + i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top')); + i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth')); + // Set rail to display:block to calculate margins + dom.css(i.scrollbarXRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + dom.css(i.scrollbarXRail, 'display', ''); + i.railXWidth = null; + i.railXRatio = null; + + i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element); + i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail); + i.scrollbarY.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarY, 'focus', focus); + i.event.bind(i.scrollbarY, 'blur', blur); + i.scrollbarYActive = null; + i.scrollbarYHeight = null; + i.scrollbarYTop = null; + i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right')); + i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN + i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left')); + i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null; + i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth')); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + dom.css(i.scrollbarYRail, 'display', ''); + i.railYHeight = null; + i.railYRatio = null; +} + +function getId(element) { + return element.getAttribute('data-ps-id'); +} + +function setId(element, id) { + element.setAttribute('data-ps-id', id); +} + +function removeId(element) { + element.removeAttribute('data-ps-id'); +} + +exports.add = function (element) { + var newId = guid(); + setId(element, newId); + instances[newId] = new Instance(element); + return instances[newId]; +}; + +exports.remove = function (element) { + delete instances[getId(element)]; + removeId(element); +}; + +exports.get = function (element) { + return instances[getId(element)]; +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateScroll = require('./update-scroll'); + +function getThumbSize(i, thumbSize) { + if (i.settings.minScrollbarLength) { + thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength); + } + if (i.settings.maxScrollbarLength) { + thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength); + } + return thumbSize; +} + +function updateCss(element, i) { + var xRailOffset = {width: i.railXWidth}; + if (i.isRtl) { + xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth; + } else { + xRailOffset.left = element.scrollLeft; + } + if (i.isScrollbarXUsingBottom) { + xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop; + } else { + xRailOffset.top = i.scrollbarXTop + element.scrollTop; + } + dom.css(i.scrollbarXRail, xRailOffset); + + var yRailOffset = {top: element.scrollTop, height: i.railYHeight}; + if (i.isScrollbarYUsingRight) { + if (i.isRtl) { + yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth; + } else { + yRailOffset.right = i.scrollbarYRight - element.scrollLeft; + } + } else { + if (i.isRtl) { + yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth; + } else { + yRailOffset.left = i.scrollbarYLeft + element.scrollLeft; + } + } + dom.css(i.scrollbarYRail, yRailOffset); + + dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth}); + dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth}); +} + +module.exports = function (element) { + var i = instances.get(element); + + i.containerWidth = element.clientWidth; + i.containerHeight = element.clientHeight; + i.contentWidth = element.scrollWidth; + i.contentHeight = element.scrollHeight; + + var existingRails; + if (!element.contains(i.scrollbarXRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarXRail, element); + } + if (!element.contains(i.scrollbarYRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarYRail, element); + } + + if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) { + i.scrollbarXActive = true; + i.railXWidth = i.containerWidth - i.railXMarginWidth; + i.railXRatio = i.containerWidth / i.railXWidth; + i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth)); + i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth)); + } else { + i.scrollbarXActive = false; + } + + if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) { + i.scrollbarYActive = true; + i.railYHeight = i.containerHeight - i.railYMarginHeight; + i.railYRatio = i.containerHeight / i.railYHeight; + i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight)); + i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight)); + } else { + i.scrollbarYActive = false; + } + + if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) { + i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth; + } + if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) { + i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight; + } + + updateCss(element, i); + + if (i.scrollbarXActive) { + cls.add(element, 'ps-active-x'); + } else { + cls.remove(element, 'ps-active-x'); + i.scrollbarXWidth = 0; + i.scrollbarXLeft = 0; + updateScroll(element, 'left', 0); + } + if (i.scrollbarYActive) { + cls.add(element, 'ps-active-y'); + } else { + cls.remove(element, 'ps-active-y'); + i.scrollbarYHeight = 0; + i.scrollbarYTop = 0; + updateScroll(element, 'top', 0); + } +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(require,module,exports){ +'use strict'; + +var instances = require('./instances'); + +var lastTop; +var lastLeft; + +var createDOMEvent = function (name) { + var event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; +}; + +module.exports = function (element, axis, value) { + if (typeof element === 'undefined') { + throw 'You must provide an element to the update-scroll function'; + } + + if (typeof axis === 'undefined') { + throw 'You must provide an axis to the update-scroll function'; + } + + if (typeof value === 'undefined') { + throw 'You must provide a value to the update-scroll function'; + } + + if (axis === 'top' && value <= 0) { + element.scrollTop = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-y-reach-start')); + } + + if (axis === 'left' && value <= 0) { + element.scrollLeft = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-x-reach-start')); + } + + var i = instances.get(element); + + if (axis === 'top' && value >= i.contentHeight - i.containerHeight) { + // don't allow scroll past container + value = i.contentHeight - i.containerHeight; + if (value - element.scrollTop <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollTop; + } else { + element.scrollTop = value; + } + element.dispatchEvent(createDOMEvent('ps-y-reach-end')); + } + + if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { + // don't allow scroll past container + value = i.contentWidth - i.containerWidth; + if (value - element.scrollLeft <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollLeft; + } else { + element.scrollLeft = value; + } + element.dispatchEvent(createDOMEvent('ps-x-reach-end')); + } + + if (!lastTop) { + lastTop = element.scrollTop; + } + + if (!lastLeft) { + lastLeft = element.scrollLeft; + } + + if (axis === 'top' && value < lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-up')); + } + + if (axis === 'top' && value > lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-down')); + } + + if (axis === 'left' && value < lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-left')); + } + + if (axis === 'left' && value > lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-right')); + } + + if (axis === 'top') { + element.scrollTop = lastTop = value; + element.dispatchEvent(createDOMEvent('ps-scroll-y')); + } + + if (axis === 'left') { + element.scrollLeft = lastLeft = value; + element.dispatchEvent(createDOMEvent('ps-scroll-x')); + } + +}; + +},{"./instances":18}],21:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); +var updateScroll = require('./update-scroll'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + // Recalcuate negative scrollLeft adjustment + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + + // Recalculate rail margins + dom.css(i.scrollbarXRail, 'display', 'block'); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + + // Hide scrollbars not to affect scrollWidth and scrollHeight + dom.css(i.scrollbarXRail, 'display', 'none'); + dom.css(i.scrollbarYRail, 'display', 'none'); + + updateGeometry(element); + + // Update top/left scroll to trigger events + updateScroll(element, 'top', element.scrollTop); + updateScroll(element, 'left', element.scrollLeft); + + dom.css(i.scrollbarXRail, 'display', ''); + dom.css(i.scrollbarYRail, 'display', ''); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); diff --git a/view/js/perfect-scrollbar/js/perfect-scrollbar.min.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.min.js new file mode 100644 index 000000000..4d76567e8 --- /dev/null +++ b/view/js/perfect-scrollbar/js/perfect-scrollbar.min.js @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.16 */ +!function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var a="function"==typeof require&&require;if(!s&&a)return a(i,!0);if(l)return l(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[i]={exports:{}};e[i][0].call(u.exports,function(t){var n=e[i][1][t];return o(n?n:t)},u,u.exports,t,e,n,r)}return n[i].exports}for(var l="function"==typeof require&&require,i=0;i=0&&n.splice(r,1),t.className=n.join(" ")}n.add=function(t,e){t.classList?t.classList.add(e):r(t,e)},n.remove=function(t,e){t.classList?t.classList.remove(e):o(t,e)},n.list=function(t){return t.classList?Array.prototype.slice.apply(t.classList):t.className.split(" ")}},{}],3:[function(t,e,n){"use strict";function r(t,e){return window.getComputedStyle(t)[e]}function o(t,e,n){return"number"==typeof n&&(n=n.toString()+"px"),t.style[e]=n,t}function l(t,e){for(var n in e){var r=e[n];"number"==typeof r&&(r=r.toString()+"px"),t.style[n]=r}return t}var i={};i.e=function(t,e){var n=document.createElement(t);return n.className=e,n},i.appendTo=function(t,e){return e.appendChild(t),t},i.css=function(t,e,n){return"object"==typeof e?l(t,e):"undefined"==typeof n?r(t,e):o(t,e,n)},i.matches=function(t,e){return"undefined"!=typeof t.matches?t.matches(e):"undefined"!=typeof t.matchesSelector?t.matchesSelector(e):"undefined"!=typeof t.webkitMatchesSelector?t.webkitMatchesSelector(e):"undefined"!=typeof t.mozMatchesSelector?t.mozMatchesSelector(e):"undefined"!=typeof t.msMatchesSelector?t.msMatchesSelector(e):void 0},i.remove=function(t){"undefined"!=typeof t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)},i.queryChildren=function(t,e){return Array.prototype.filter.call(t.childNodes,function(t){return i.matches(t,e)})},e.exports=i},{}],4:[function(t,e,n){"use strict";var r=function(t){this.element=t,this.events={}};r.prototype.bind=function(t,e){"undefined"==typeof this.events[t]&&(this.events[t]=[]),this.events[t].push(e),this.element.addEventListener(t,e,!1)},r.prototype.unbind=function(t,e){var n="undefined"!=typeof e;this.events[t]=this.events[t].filter(function(r){return!(!n||r===e)||(this.element.removeEventListener(t,r,!1),!1)},this)},r.prototype.unbindAll=function(){for(var t in this.events)this.unbind(t)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(t){var e=this.eventElements.filter(function(e){return e.element===t})[0];return"undefined"==typeof e&&(e=new r(t),this.eventElements.push(e)),e},o.prototype.bind=function(t,e,n){this.eventElement(t).bind(e,n)},o.prototype.unbind=function(t,e,n){this.eventElement(t).unbind(e,n)},o.prototype.unbindAll=function(){for(var t=0;te.scrollbarYTop?1:-1;i(t,"top",t.scrollTop+s*e.containerHeight),l(t),r.stopPropagation()}),e.event.bind(e.scrollbarX,"click",r),e.event.bind(e.scrollbarXRail,"click",function(r){var o=r.pageX-window.pageXOffset-n(e.scrollbarXRail).left,s=o>e.scrollbarXLeft?1:-1;i(t,"left",t.scrollLeft+s*e.containerWidth),l(t),r.stopPropagation()})}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(t,e,n){"use strict";function r(t,e){function n(n){var o=r+n*e.railXRatio,i=Math.max(0,e.scrollbarXRail.getBoundingClientRect().left)+e.railXRatio*(e.railXWidth-e.scrollbarXWidth);o<0?e.scrollbarXLeft=0:o>i?e.scrollbarXLeft=i:e.scrollbarXLeft=o;var s=l.toInt(e.scrollbarXLeft*(e.contentWidth-e.containerWidth)/(e.containerWidth-e.railXRatio*e.scrollbarXWidth))-e.negativeScrollAdjustment;c(t,"left",s)}var r=null,o=null,s=function(e){n(e.pageX-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"x"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarX,"mousedown",function(n){o=n.pageX,r=l.toInt(i.css(e.scrollbarX,"left"))*e.railXRatio,l.startScrolling(t,"x"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}function o(t,e){function n(n){var o=r+n*e.railYRatio,i=Math.max(0,e.scrollbarYRail.getBoundingClientRect().top)+e.railYRatio*(e.railYHeight-e.scrollbarYHeight);o<0?e.scrollbarYTop=0:o>i?e.scrollbarYTop=i:e.scrollbarYTop=o;var s=l.toInt(e.scrollbarYTop*(e.contentHeight-e.containerHeight)/(e.containerHeight-e.railYRatio*e.scrollbarYHeight));c(t,"top",s)}var r=null,o=null,s=function(e){n(e.pageY-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"y"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarY,"mousedown",function(n){o=n.pageY,r=l.toInt(i.css(e.scrollbarY,"top"))*e.railYRatio,l.startScrolling(t,"y"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}var l=t("../../lib/helper"),i=t("../../lib/dom"),s=t("../instances"),a=t("../update-geometry"),c=t("../update-scroll");e.exports=function(t){var e=s.get(t);r(t,e),o(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}var r=!1;e.event.bind(t,"mouseenter",function(){r=!0}),e.event.bind(t,"mouseleave",function(){r=!1});var i=!1;e.event.bind(e.ownerDocument,"keydown",function(c){if(!(c.isDefaultPrevented&&c.isDefaultPrevented()||c.defaultPrevented)){var u=l.matches(e.scrollbarX,":focus")||l.matches(e.scrollbarY,":focus");if(r||u){var d=document.activeElement?document.activeElement:e.ownerDocument.activeElement;if(d){if("IFRAME"===d.tagName)d=d.contentDocument.activeElement;else for(;d.shadowRoot;)d=d.shadowRoot.activeElement;if(o.isEditable(d))return}var p=0,f=0;switch(c.which){case 37:p=c.metaKey?-e.contentWidth:c.altKey?-e.containerWidth:-30;break;case 38:f=c.metaKey?e.contentHeight:c.altKey?e.containerHeight:30;break;case 39:p=c.metaKey?e.contentWidth:c.altKey?e.containerWidth:30;break;case 40:f=c.metaKey?-e.contentHeight:c.altKey?-e.containerHeight:-30;break;case 33:f=90;break;case 32:f=c.shiftKey?90:-90;break;case 34:f=-90;break;case 35:f=c.ctrlKey?-e.contentHeight:-e.containerHeight;break;case 36:f=c.ctrlKey?t.scrollTop:e.containerHeight;break;default:return}a(t,"top",t.scrollTop-f),a(t,"left",t.scrollLeft+p),s(t),i=n(p,f),i&&c.preventDefault()}}})}var o=t("../../lib/helper"),l=t("../../lib/dom"),i=t("../instances"),s=t("../update-geometry"),a=t("../update-scroll");e.exports=function(t){var e=i.get(t);r(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}function r(t){var e=t.deltaX,n=-1*t.deltaY;return"undefined"!=typeof e&&"undefined"!=typeof n||(e=-1*t.wheelDeltaX/6,n=t.wheelDeltaY/6),t.deltaMode&&1===t.deltaMode&&(e*=10,n*=10),e!==e&&n!==n&&(e=0,n=t.wheelDelta),t.shiftKey?[-n,-e]:[e,n]}function o(e,n){var r=t.querySelector("textarea:hover, select[multiple]:hover, .ps-child:hover");if(r){if(!window.getComputedStyle(r).overflow.match(/(scroll|auto)/))return!1;var o=r.scrollHeight-r.clientHeight;if(o>0&&!(0===r.scrollTop&&n>0||r.scrollTop===o&&n<0))return!0;var l=r.scrollLeft-r.clientWidth;if(l>0&&!(0===r.scrollLeft&&e<0||r.scrollLeft===l&&e>0))return!0}return!1}function s(s){var c=r(s),u=c[0],d=c[1];o(u,d)||(a=!1,e.settings.useBothWheelAxes?e.scrollbarYActive&&!e.scrollbarXActive?(d?i(t,"top",t.scrollTop-d*e.settings.wheelSpeed):i(t,"top",t.scrollTop+u*e.settings.wheelSpeed),a=!0):e.scrollbarXActive&&!e.scrollbarYActive&&(u?i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed):i(t,"left",t.scrollLeft-d*e.settings.wheelSpeed),a=!0):(i(t,"top",t.scrollTop-d*e.settings.wheelSpeed),i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed)),l(t),a=a||n(u,d),a&&(s.stopPropagation(),s.preventDefault()))}var a=!1;"undefined"!=typeof window.onwheel?e.event.bind(t,"wheel",s):"undefined"!=typeof window.onmousewheel&&e.event.bind(t,"mousewheel",s)}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(t,e,n){"use strict";function r(t,e){e.event.bind(t,"scroll",function(){l(t)})}var o=t("../instances"),l=t("../update-geometry");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19}],15:[function(t,e,n){"use strict";function r(t,e){function n(){var t=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===t.toString().length?null:t.getRangeAt(0).commonAncestorContainer}function r(){c||(c=setInterval(function(){return l.get(t)?(s(t,"top",t.scrollTop+u.top),s(t,"left",t.scrollLeft+u.left),void i(t)):void clearInterval(c)},50))}function a(){c&&(clearInterval(c),c=null),o.stopScrolling(t)}var c=null,u={top:0,left:0},d=!1;e.event.bind(e.ownerDocument,"selectionchange",function(){t.contains(n())?d=!0:(d=!1,a())}),e.event.bind(window,"mouseup",function(){d&&(d=!1,a())}),e.event.bind(window,"keyup",function(){d&&(d=!1,a())}),e.event.bind(window,"mousemove",function(e){if(d){var n={x:e.pageX,y:e.pageY},l={left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,top:t.offsetTop,bottom:t.offsetTop+t.offsetHeight};n.xl.right-3?(u.left=5,o.startScrolling(t,"x")):u.left=0,n.yl.bottom-3?(n.y-l.bottom+3<5?u.top=5:u.top=20,o.startScrolling(t,"y")):u.top=0,0===u.top&&0===u.left?a():r()}})}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){var e=l.get(t);r(t,e)}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(t,e,n){"use strict";function r(t,e,n,r){function o(n,r){var o=t.scrollTop,l=t.scrollLeft,i=Math.abs(n),s=Math.abs(r);if(s>i){if(r<0&&o===e.contentHeight-e.containerHeight||r>0&&0===o)return!e.settings.swipePropagation}else if(i>s&&(n<0&&l===e.contentWidth-e.containerWidth||n>0&&0===l))return!e.settings.swipePropagation;return!0}function a(e,n){s(t,"top",t.scrollTop-n),s(t,"left",t.scrollLeft-e),i(t)}function c(){w=!0}function u(){w=!1}function d(t){return t.targetTouches?t.targetTouches[0]:t}function p(t){return!(!t.targetTouches||1!==t.targetTouches.length)||!(!t.pointerType||"mouse"===t.pointerType||t.pointerType===t.MSPOINTER_TYPE_MOUSE)}function f(t){if(p(t)){Y=!0;var e=d(t);g.pageX=e.pageX,g.pageY=e.pageY,v=(new Date).getTime(),null!==y&&clearInterval(y),t.stopPropagation()}}function h(t){if(!Y&&e.settings.swipePropagation&&f(t),!w&&Y&&p(t)){var n=d(t),r={pageX:n.pageX,pageY:n.pageY},l=r.pageX-g.pageX,i=r.pageY-g.pageY;a(l,i),g=r;var s=(new Date).getTime(),c=s-v;c>0&&(m.x=l/c,m.y=i/c,v=s),o(l,i)&&(t.stopPropagation(),t.preventDefault())}}function b(){!w&&Y&&(Y=!1,clearInterval(y),y=setInterval(function(){return l.get(t)&&(m.x||m.y)?Math.abs(m.x)<.01&&Math.abs(m.y)<.01?void clearInterval(y):(a(30*m.x,30*m.y),m.x*=.8,void(m.y*=.8)):void clearInterval(y)},10))}var g={},v=0,m={},y=null,w=!1,Y=!1;n?(e.event.bind(window,"touchstart",c),e.event.bind(window,"touchend",u),e.event.bind(t,"touchstart",f),e.event.bind(t,"touchmove",h),e.event.bind(t,"touchend",b)):r&&(window.PointerEvent?(e.event.bind(window,"pointerdown",c),e.event.bind(window,"pointerup",u),e.event.bind(t,"pointerdown",f),e.event.bind(t,"pointermove",h),e.event.bind(t,"pointerup",b)):window.MSPointerEvent&&(e.event.bind(window,"MSPointerDown",c),e.event.bind(window,"MSPointerUp",u),e.event.bind(t,"MSPointerDown",f),e.event.bind(t,"MSPointerMove",h),e.event.bind(t,"MSPointerUp",b)))}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){if(o.env.supportsTouch||o.env.supportsIePointer){var e=l.get(t);r(t,e,o.env.supportsTouch,o.env.supportsIePointer)}}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/class"),l=t("./instances"),i=t("./update-geometry"),s={"click-rail":t("./handler/click-rail"),"drag-scrollbar":t("./handler/drag-scrollbar"),keyboard:t("./handler/keyboard"),wheel:t("./handler/mouse-wheel"),touch:t("./handler/touch"),selection:t("./handler/selection")},a=t("./handler/native-scroll");e.exports=function(t,e){e="object"==typeof e?e:{},o.add(t,"ps-container");var n=l.add(t);n.settings=r.extend(n.settings,e),o.add(t,"ps-theme-"+n.settings.theme),n.settings.handlers.forEach(function(e){s[e](t)}),a(t),i(t)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(t,e,n){"use strict";function r(t){function e(){a.add(t,"ps-focus")}function n(){a.remove(t,"ps-focus")}var r=this;r.settings=s.clone(c),r.containerWidth=null,r.containerHeight=null,r.contentWidth=null,r.contentHeight=null,r.isRtl="rtl"===u.css(t,"direction"),r.isNegativeScroll=function(){var e=t.scrollLeft,n=null;return t.scrollLeft=-1,n=t.scrollLeft<0,t.scrollLeft=e,n}(),r.negativeScrollAdjustment=r.isNegativeScroll?t.scrollWidth-t.clientWidth:0,r.event=new d,r.ownerDocument=t.ownerDocument||document,r.scrollbarXRail=u.appendTo(u.e("div","ps-scrollbar-x-rail"),t),r.scrollbarX=u.appendTo(u.e("div","ps-scrollbar-x"),r.scrollbarXRail),r.scrollbarX.setAttribute("tabindex",0),r.event.bind(r.scrollbarX,"focus",e),r.event.bind(r.scrollbarX,"blur",n),r.scrollbarXActive=null,r.scrollbarXWidth=null,r.scrollbarXLeft=null,r.scrollbarXBottom=s.toInt(u.css(r.scrollbarXRail,"bottom")),r.isScrollbarXUsingBottom=r.scrollbarXBottom===r.scrollbarXBottom,r.scrollbarXTop=r.isScrollbarXUsingBottom?null:s.toInt(u.css(r.scrollbarXRail,"top")),r.railBorderXWidth=s.toInt(u.css(r.scrollbarXRail,"borderLeftWidth"))+s.toInt(u.css(r.scrollbarXRail,"borderRightWidth")),u.css(r.scrollbarXRail,"display","block"),r.railXMarginWidth=s.toInt(u.css(r.scrollbarXRail,"marginLeft"))+s.toInt(u.css(r.scrollbarXRail,"marginRight")),u.css(r.scrollbarXRail,"display",""),r.railXWidth=null,r.railXRatio=null,r.scrollbarYRail=u.appendTo(u.e("div","ps-scrollbar-y-rail"),t),r.scrollbarY=u.appendTo(u.e("div","ps-scrollbar-y"),r.scrollbarYRail),r.scrollbarY.setAttribute("tabindex",0),r.event.bind(r.scrollbarY,"focus",e),r.event.bind(r.scrollbarY,"blur",n),r.scrollbarYActive=null,r.scrollbarYHeight=null,r.scrollbarYTop=null,r.scrollbarYRight=s.toInt(u.css(r.scrollbarYRail,"right")),r.isScrollbarYUsingRight=r.scrollbarYRight===r.scrollbarYRight,r.scrollbarYLeft=r.isScrollbarYUsingRight?null:s.toInt(u.css(r.scrollbarYRail,"left")),r.scrollbarYOuterWidth=r.isRtl?s.outerWidth(r.scrollbarY):null,r.railBorderYWidth=s.toInt(u.css(r.scrollbarYRail,"borderTopWidth"))+s.toInt(u.css(r.scrollbarYRail,"borderBottomWidth")),u.css(r.scrollbarYRail,"display","block"),r.railYMarginHeight=s.toInt(u.css(r.scrollbarYRail,"marginTop"))+s.toInt(u.css(r.scrollbarYRail,"marginBottom")),u.css(r.scrollbarYRail,"display",""),r.railYHeight=null,r.railYRatio=null}function o(t){return t.getAttribute("data-ps-id")}function l(t,e){t.setAttribute("data-ps-id",e)}function i(t){t.removeAttribute("data-ps-id")}var s=t("../lib/helper"),a=t("../lib/class"),c=t("./default-setting"),u=t("../lib/dom"),d=t("../lib/event-manager"),p=t("../lib/guid"),f={};n.add=function(t){var e=p();return l(t,e),f[e]=new r(t),f[e]},n.remove=function(t){delete f[o(t)],i(t)},n.get=function(t){return f[o(t)]}},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(t,e,n){"use strict";function r(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.settings.minScrollbarLength)),t.settings.maxScrollbarLength&&(e=Math.min(e,t.settings.maxScrollbarLength)),e}function o(t,e){var n={width:e.railXWidth};e.isRtl?n.left=e.negativeScrollAdjustment+t.scrollLeft+e.containerWidth-e.contentWidth:n.left=t.scrollLeft,e.isScrollbarXUsingBottom?n.bottom=e.scrollbarXBottom-t.scrollTop:n.top=e.scrollbarXTop+t.scrollTop,s.css(e.scrollbarXRail,n);var r={top:t.scrollTop,height:e.railYHeight};e.isScrollbarYUsingRight?e.isRtl?r.right=e.contentWidth-(e.negativeScrollAdjustment+t.scrollLeft)-e.scrollbarYRight-e.scrollbarYOuterWidth:r.right=e.scrollbarYRight-t.scrollLeft:e.isRtl?r.left=e.negativeScrollAdjustment+t.scrollLeft+2*e.containerWidth-e.contentWidth-e.scrollbarYLeft-e.scrollbarYOuterWidth:r.left=e.scrollbarYLeft+t.scrollLeft,s.css(e.scrollbarYRail,r),s.css(e.scrollbarX,{left:e.scrollbarXLeft,width:e.scrollbarXWidth-e.railBorderXWidth}),s.css(e.scrollbarY,{top:e.scrollbarYTop,height:e.scrollbarYHeight-e.railBorderYWidth})}var l=t("../lib/helper"),i=t("../lib/class"),s=t("../lib/dom"),a=t("./instances"),c=t("./update-scroll");e.exports=function(t){var e=a.get(t);e.containerWidth=t.clientWidth,e.containerHeight=t.clientHeight,e.contentWidth=t.scrollWidth,e.contentHeight=t.scrollHeight;var n;t.contains(e.scrollbarXRail)||(n=s.queryChildren(t,".ps-scrollbar-x-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarXRail,t)),t.contains(e.scrollbarYRail)||(n=s.queryChildren(t,".ps-scrollbar-y-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarYRail,t)),!e.settings.suppressScrollX&&e.containerWidth+e.settings.scrollXMarginOffset=e.railXWidth-e.scrollbarXWidth&&(e.scrollbarXLeft=e.railXWidth-e.scrollbarXWidth),e.scrollbarYTop>=e.railYHeight-e.scrollbarYHeight&&(e.scrollbarYTop=e.railYHeight-e.scrollbarYHeight),o(t,e),e.scrollbarXActive?i.add(t,"ps-active-x"):(i.remove(t,"ps-active-x"),e.scrollbarXWidth=0,e.scrollbarXLeft=0,c(t,"left",0)),e.scrollbarYActive?i.add(t,"ps-active-y"):(i.remove(t,"ps-active-y"),e.scrollbarYHeight=0,e.scrollbarYTop=0,c(t,"top",0))}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(t,e,n){"use strict";var r,o,l=t("./instances"),i=function(t){var e=document.createEvent("Event");return e.initEvent(t,!0,!0),e};e.exports=function(t,e,n){if("undefined"==typeof t)throw"You must provide an element to the update-scroll function";if("undefined"==typeof e)throw"You must provide an axis to the update-scroll function";if("undefined"==typeof n)throw"You must provide a value to the update-scroll function";"top"===e&&n<=0&&(t.scrollTop=n=0,t.dispatchEvent(i("ps-y-reach-start"))),"left"===e&&n<=0&&(t.scrollLeft=n=0,t.dispatchEvent(i("ps-x-reach-start")));var s=l.get(t);"top"===e&&n>=s.contentHeight-s.containerHeight&&(n=s.contentHeight-s.containerHeight,n-t.scrollTop<=1?n=t.scrollTop:t.scrollTop=n,t.dispatchEvent(i("ps-y-reach-end"))),"left"===e&&n>=s.contentWidth-s.containerWidth&&(n=s.contentWidth-s.containerWidth,n-t.scrollLeft<=1?n=t.scrollLeft:t.scrollLeft=n,t.dispatchEvent(i("ps-x-reach-end"))),r||(r=t.scrollTop),o||(o=t.scrollLeft),"top"===e&&nr&&t.dispatchEvent(i("ps-scroll-down")),"left"===e&&no&&t.dispatchEvent(i("ps-scroll-right")),"top"===e&&(t.scrollTop=r=n,t.dispatchEvent(i("ps-scroll-y"))),"left"===e&&(t.scrollLeft=o=n,t.dispatchEvent(i("ps-scroll-x")))}},{"./instances":18}],21:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances"),i=t("./update-geometry"),s=t("./update-scroll");e.exports=function(t){var e=l.get(t);e&&(e.negativeScrollAdjustment=e.isNegativeScroll?t.scrollWidth-t.clientWidth:0,o.css(e.scrollbarXRail,"display","block"),o.css(e.scrollbarYRail,"display","block"),e.railXMarginWidth=r.toInt(o.css(e.scrollbarXRail,"marginLeft"))+r.toInt(o.css(e.scrollbarXRail,"marginRight")),e.railYMarginHeight=r.toInt(o.css(e.scrollbarYRail,"marginTop"))+r.toInt(o.css(e.scrollbarYRail,"marginBottom")),o.css(e.scrollbarXRail,"display","none"),o.css(e.scrollbarYRail,"display","none"),i(t),s(t,"top",t.scrollTop),s(t,"left",t.scrollLeft),o.css(e.scrollbarXRail,"display",""),o.css(e.scrollbarYRail,"display",""))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); \ No newline at end of file diff --git a/view/js/perfect-scrollbar/src/css/main.scss b/view/js/perfect-scrollbar/src/css/main.scss new file mode 100644 index 000000000..ae1f655ad --- /dev/null +++ b/view/js/perfect-scrollbar/src/css/main.scss @@ -0,0 +1,3 @@ +@import 'variables'; +@import 'mixins'; +@import 'themes'; diff --git a/view/js/perfect-scrollbar/src/css/mixins.scss b/view/js/perfect-scrollbar/src/css/mixins.scss new file mode 100644 index 000000000..79d3d2c9d --- /dev/null +++ b/view/js/perfect-scrollbar/src/css/mixins.scss @@ -0,0 +1,128 @@ +@mixin scrollbar-rail-default($theme) { + display: none; + position: absolute; /* please don't change 'position' */ + opacity: map_get($theme, rail-default-opacity); + transition: background-color .2s linear, opacity .2s linear; +} + +@mixin scrollbar-rail-hover($theme) { + background-color: map_get($theme, rail-hover-bg); + opacity: map_get($theme, rail-hover-opacity); +} + +@mixin scrollbar-default($theme) { + position: absolute; /* please don't change 'position' */ + background-color: map_get($theme, bar-container-hover-bg); + border-radius: map_get($theme, border-radius); + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, + border-radius .2s ease-in-out; +} + +@mixin scrollbar-hover($theme) { + background-color: map_get($theme, bar-hover-bg); +} + +@mixin in-scrolling($theme) { + &.ps-in-scrolling { + &.ps-x > .ps-scrollbar-x-rail { + @include scrollbar-rail-hover($theme); + > .ps-scrollbar-x { + @include scrollbar-hover($theme); + height: map_get($theme, scrollbar-x-hover-height); + } + } + &.ps-y > .ps-scrollbar-y-rail { + @include scrollbar-rail-hover($theme); + > .ps-scrollbar-y { + @include scrollbar-hover($theme); + width: map_get($theme, scrollbar-y-hover-width); + } + } + } +} + +// Layout and theme mixin +@mixin ps-container($theme) { + -ms-touch-action: auto; + touch-action: auto; + overflow: hidden !important; + -ms-overflow-style: none; + + // Edge + @supports (-ms-overflow-style: none) { + overflow: auto !important; + } + // IE10+ + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + overflow: auto !important; + } + + &.ps-active-x > .ps-scrollbar-x-rail, + &.ps-active-y > .ps-scrollbar-y-rail { + display: block; + background-color: map_get($theme, bar-bg); + } + + @include in-scrolling($theme); + + > .ps-scrollbar-x-rail { + @include scrollbar-rail-default($theme); + bottom: map_get($theme, scrollbar-x-rail-bottom); /* there must be 'bottom' for ps-scrollbar-x-rail */ + height: map_get($theme, scrollbar-x-rail-height); + + > .ps-scrollbar-x { + @include scrollbar-default($theme); + bottom: map_get($theme, scrollbar-x-bottom); /* there must be 'bottom' for ps-scrollbar-x */ + height: map_get($theme, scrollbar-x-height); + } + &:hover, + &:active { + > .ps-scrollbar-x { + height: map_get($theme, scrollbar-x-hover-height); + } + } + } + + > .ps-scrollbar-y-rail { + @include scrollbar-rail-default($theme); + right: map_get($theme, scrollbar-y-rail-right); /* there must be 'right' for ps-scrollbar-y-rail */ + width: map_get($theme, scrollbar-y-rail-width); + + > .ps-scrollbar-y { + @include scrollbar-default($theme); + right: map_get($theme, scrollbar-y-right); /* there must be 'right' for ps-scrollbar-y */ + width: map_get($theme, scrollbar-y-width); + } + &:hover, + &:active { + > .ps-scrollbar-y { + width: map_get($theme, scrollbar-y-hover-width); + } + } + } + + &:hover { + @include in-scrolling($theme); + + > .ps-scrollbar-x-rail, + > .ps-scrollbar-y-rail { + opacity: map_get($theme, rail-container-hover-opacity); + } + + > .ps-scrollbar-x-rail:hover { + @include scrollbar-rail-hover($theme); + + > .ps-scrollbar-x { + @include scrollbar-hover($theme); + } + } + + > .ps-scrollbar-y-rail:hover { + @include scrollbar-rail-hover($theme); + + > .ps-scrollbar-y { + @include scrollbar-hover($theme); + } + } + } +} diff --git a/view/js/perfect-scrollbar/src/css/themes.scss b/view/js/perfect-scrollbar/src/css/themes.scss new file mode 100644 index 000000000..bf7729a7d --- /dev/null +++ b/view/js/perfect-scrollbar/src/css/themes.scss @@ -0,0 +1,25 @@ +$ps-theme-default: ( + border-radius: $ps-border-radius, + rail-default-opacity: $ps-rail-default-opacity, + rail-container-hover-opacity: $ps-rail-container-hover-opacity, + rail-hover-opacity: $ps-rail-hover-opacity, + bar-bg: $ps-bar-bg, + bar-container-hover-bg: $ps-bar-container-hover-bg, + bar-hover-bg: $ps-bar-hover-bg, + rail-hover-bg: $ps-rail-hover-bg, + scrollbar-x-rail-bottom: $ps-scrollbar-x-rail-bottom, + scrollbar-x-rail-height: $ps-scrollbar-x-rail-height, + scrollbar-x-bottom: $ps-scrollbar-x-bottom, + scrollbar-x-height: $ps-scrollbar-x-height, + scrollbar-x-hover-height: $ps-scrollbar-x-hover-height, + scrollbar-y-rail-right: $ps-scrollbar-y-rail-right, + scrollbar-y-rail-width: $ps-scrollbar-y-rail-width, + scrollbar-y-right: $ps-scrollbar-y-right, + scrollbar-y-width: $ps-scrollbar-y-width, + scrollbar-y-hover-width: $ps-scrollbar-y-hover-width, +); + +// Default theme +.ps-container { + @include ps-container($ps-theme-default); +} diff --git a/view/js/perfect-scrollbar/src/css/variables.scss b/view/js/perfect-scrollbar/src/css/variables.scss new file mode 100644 index 000000000..7454fb0b0 --- /dev/null +++ b/view/js/perfect-scrollbar/src/css/variables.scss @@ -0,0 +1,24 @@ +// Colors +$ps-border-radius: 6px !default; + +$ps-rail-default-opacity: 0 !default; +$ps-rail-container-hover-opacity: 0.6 !default; +$ps-rail-hover-opacity: 0.9 !default; + +$ps-bar-bg: transparent !default; +$ps-bar-container-hover-bg: #aaa !default; +$ps-bar-hover-bg: #999 !default; +$ps-rail-hover-bg: #eee !default; + +// Sizes +$ps-scrollbar-x-rail-bottom: 0px !default; +$ps-scrollbar-x-rail-height: 15px !default; +$ps-scrollbar-x-bottom: 2px !default; +$ps-scrollbar-x-height: 6px !default; +$ps-scrollbar-x-hover-height: 11px !default; + +$ps-scrollbar-y-rail-right: 0 !default; +$ps-scrollbar-y-rail-width: 15px !default; +$ps-scrollbar-y-right: 2px !default; +$ps-scrollbar-y-width: 6px !default; +$ps-scrollbar-y-hover-width: 11px !default; diff --git a/view/js/perfect-scrollbar/src/js/adaptor/global.js b/view/js/perfect-scrollbar/src/js/adaptor/global.js new file mode 100644 index 000000000..0438e361e --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/adaptor/global.js @@ -0,0 +1,14 @@ +'use strict'; + +var ps = require('../main'); + +if (typeof define === 'function' && define.amd) { + // AMD + define(ps); +} else { + // Add to a global object. + window.PerfectScrollbar = ps; + if (typeof window.Ps === 'undefined') { + window.Ps = ps; + } +} diff --git a/view/js/perfect-scrollbar/src/js/adaptor/jquery.js b/view/js/perfect-scrollbar/src/js/adaptor/jquery.js new file mode 100644 index 000000000..ef55e093e --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/adaptor/jquery.js @@ -0,0 +1,41 @@ +'use strict'; + +var ps = require('../main'); +var psInstances = require('../plugin/instances'); + +function mountJQuery(jQuery) { + jQuery.fn.perfectScrollbar = function (settingOrCommand) { + return this.each(function () { + if (typeof settingOrCommand === 'object' || + typeof settingOrCommand === 'undefined') { + // If it's an object or none, initialize. + var settings = settingOrCommand; + + if (!psInstances.get(this)) { + ps.initialize(this, settings); + } + } else { + // Unless, it may be a command. + var command = settingOrCommand; + + if (command === 'update') { + ps.update(this); + } else if (command === 'destroy') { + ps.destroy(this); + } + } + }); + }; +} + +if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], mountJQuery); +} else { + var jq = window.jQuery ? window.jQuery : window.$; + if (typeof jq !== 'undefined') { + mountJQuery(jq); + } +} + +module.exports = mountJQuery; diff --git a/view/js/perfect-scrollbar/src/js/lib/class.js b/view/js/perfect-scrollbar/src/js/lib/class.js new file mode 100644 index 000000000..951b10bb2 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/lib/class.js @@ -0,0 +1,42 @@ +'use strict'; + +function oldAdd(element, className) { + var classes = element.className.split(' '); + if (classes.indexOf(className) < 0) { + classes.push(className); + } + element.className = classes.join(' '); +} + +function oldRemove(element, className) { + var classes = element.className.split(' '); + var idx = classes.indexOf(className); + if (idx >= 0) { + classes.splice(idx, 1); + } + element.className = classes.join(' '); +} + +exports.add = function (element, className) { + if (element.classList) { + element.classList.add(className); + } else { + oldAdd(element, className); + } +}; + +exports.remove = function (element, className) { + if (element.classList) { + element.classList.remove(className); + } else { + oldRemove(element, className); + } +}; + +exports.list = function (element) { + if (element.classList) { + return Array.prototype.slice.apply(element.classList); + } else { + return element.className.split(' '); + } +}; diff --git a/view/js/perfect-scrollbar/src/js/lib/dom.js b/view/js/perfect-scrollbar/src/js/lib/dom.js new file mode 100644 index 000000000..b929a17ed --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/lib/dom.js @@ -0,0 +1,84 @@ +'use strict'; + +var DOM = {}; + +DOM.e = function (tagName, className) { + var element = document.createElement(tagName); + element.className = className; + return element; +}; + +DOM.appendTo = function (child, parent) { + parent.appendChild(child); + return child; +}; + +function cssGet(element, styleName) { + return window.getComputedStyle(element)[styleName]; +} + +function cssSet(element, styleName, styleValue) { + if (typeof styleValue === 'number') { + styleValue = styleValue.toString() + 'px'; + } + element.style[styleName] = styleValue; + return element; +} + +function cssMultiSet(element, obj) { + for (var key in obj) { + var val = obj[key]; + if (typeof val === 'number') { + val = val.toString() + 'px'; + } + element.style[key] = val; + } + return element; +} + +DOM.css = function (element, styleNameOrObject, styleValue) { + if (typeof styleNameOrObject === 'object') { + // multiple set with object + return cssMultiSet(element, styleNameOrObject); + } else { + if (typeof styleValue === 'undefined') { + return cssGet(element, styleNameOrObject); + } else { + return cssSet(element, styleNameOrObject, styleValue); + } + } +}; + +DOM.matches = function (element, query) { + if (typeof element.matches !== 'undefined') { + return element.matches(query); + } else { + if (typeof element.matchesSelector !== 'undefined') { + return element.matchesSelector(query); + } else if (typeof element.webkitMatchesSelector !== 'undefined') { + return element.webkitMatchesSelector(query); + } else if (typeof element.mozMatchesSelector !== 'undefined') { + return element.mozMatchesSelector(query); + } else if (typeof element.msMatchesSelector !== 'undefined') { + return element.msMatchesSelector(query); + } + } +}; + +DOM.remove = function (element) { + if (typeof element.remove !== 'undefined') { + element.remove(); + } else { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + } +}; + +DOM.queryChildren = function (element, selector) { + return Array.prototype.filter.call(element.childNodes, function (child) { + return DOM.matches(child, selector); + }); +}; + +module.exports = DOM; diff --git a/view/js/perfect-scrollbar/src/js/lib/event-manager.js b/view/js/perfect-scrollbar/src/js/lib/event-manager.js new file mode 100644 index 000000000..d148ad8fe --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/lib/event-manager.js @@ -0,0 +1,71 @@ +'use strict'; + +var EventElement = function (element) { + this.element = element; + this.events = {}; +}; + +EventElement.prototype.bind = function (eventName, handler) { + if (typeof this.events[eventName] === 'undefined') { + this.events[eventName] = []; + } + this.events[eventName].push(handler); + this.element.addEventListener(eventName, handler, false); +}; + +EventElement.prototype.unbind = function (eventName, handler) { + var isHandlerProvided = (typeof handler !== 'undefined'); + this.events[eventName] = this.events[eventName].filter(function (hdlr) { + if (isHandlerProvided && hdlr !== handler) { + return true; + } + this.element.removeEventListener(eventName, hdlr, false); + return false; + }, this); +}; + +EventElement.prototype.unbindAll = function () { + for (var name in this.events) { + this.unbind(name); + } +}; + +var EventManager = function () { + this.eventElements = []; +}; + +EventManager.prototype.eventElement = function (element) { + var ee = this.eventElements.filter(function (eventElement) { + return eventElement.element === element; + })[0]; + if (typeof ee === 'undefined') { + ee = new EventElement(element); + this.eventElements.push(ee); + } + return ee; +}; + +EventManager.prototype.bind = function (element, eventName, handler) { + this.eventElement(element).bind(eventName, handler); +}; + +EventManager.prototype.unbind = function (element, eventName, handler) { + this.eventElement(element).unbind(eventName, handler); +}; + +EventManager.prototype.unbindAll = function () { + for (var i = 0; i < this.eventElements.length; i++) { + this.eventElements[i].unbindAll(); + } +}; + +EventManager.prototype.once = function (element, eventName, handler) { + var ee = this.eventElement(element); + var onceHandler = function (e) { + ee.unbind(eventName, onceHandler); + handler(e); + }; + ee.bind(eventName, onceHandler); +}; + +module.exports = EventManager; diff --git a/view/js/perfect-scrollbar/src/js/lib/guid.js b/view/js/perfect-scrollbar/src/js/lib/guid.js new file mode 100644 index 000000000..84c7237eb --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/lib/guid.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; +})(); diff --git a/view/js/perfect-scrollbar/src/js/lib/helper.js b/view/js/perfect-scrollbar/src/js/lib/helper.js new file mode 100644 index 000000000..a72f2e59f --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/lib/helper.js @@ -0,0 +1,83 @@ +'use strict'; + +var cls = require('./class'); +var dom = require('./dom'); + +var toInt = exports.toInt = function (x) { + return parseInt(x, 10) || 0; +}; + +var clone = exports.clone = function (obj) { + if (!obj) { + return null; + } else if (obj.constructor === Array) { + return obj.map(clone); + } else if (typeof obj === 'object') { + var result = {}; + for (var key in obj) { + result[key] = clone(obj[key]); + } + return result; + } else { + return obj; + } +}; + +exports.extend = function (original, source) { + var result = clone(original); + for (var key in source) { + result[key] = clone(source[key]); + } + return result; +}; + +exports.isEditable = function (el) { + return dom.matches(el, "input,[contenteditable]") || + dom.matches(el, "select,[contenteditable]") || + dom.matches(el, "textarea,[contenteditable]") || + dom.matches(el, "button,[contenteditable]"); +}; + +exports.removePsClasses = function (element) { + var clsList = cls.list(element); + for (var i = 0; i < clsList.length; i++) { + var className = clsList[i]; + if (className.indexOf('ps-') === 0) { + cls.remove(element, className); + } + } +}; + +exports.outerWidth = function (element) { + return toInt(dom.css(element, 'width')) + + toInt(dom.css(element, 'paddingLeft')) + + toInt(dom.css(element, 'paddingRight')) + + toInt(dom.css(element, 'borderLeftWidth')) + + toInt(dom.css(element, 'borderRightWidth')); +}; + +exports.startScrolling = function (element, axis) { + cls.add(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.add(element, 'ps-' + axis); + } else { + cls.add(element, 'ps-x'); + cls.add(element, 'ps-y'); + } +}; + +exports.stopScrolling = function (element, axis) { + cls.remove(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.remove(element, 'ps-' + axis); + } else { + cls.remove(element, 'ps-x'); + cls.remove(element, 'ps-y'); + } +}; + +exports.env = { + isWebKit: 'WebkitAppearance' in document.documentElement.style, + supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + supportsIePointer: window.navigator.msMaxTouchPoints !== null +}; diff --git a/view/js/perfect-scrollbar/src/js/main.js b/view/js/perfect-scrollbar/src/js/main.js new file mode 100644 index 000000000..06b1c2be7 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/main.js @@ -0,0 +1,11 @@ +'use strict'; + +var destroy = require('./plugin/destroy'); +var initialize = require('./plugin/initialize'); +var update = require('./plugin/update'); + +module.exports = { + initialize: initialize, + update: update, + destroy: destroy +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/default-setting.js b/view/js/perfect-scrollbar/src/js/plugin/default-setting.js new file mode 100644 index 000000000..b3f2ddd8b --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/default-setting.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = { + handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'], + maxScrollbarLength: null, + minScrollbarLength: null, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + suppressScrollX: false, + suppressScrollY: false, + swipePropagation: true, + useBothWheelAxes: false, + wheelPropagation: false, + wheelSpeed: 1, + theme: 'default' +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/destroy.js b/view/js/perfect-scrollbar/src/js/plugin/destroy.js new file mode 100644 index 000000000..97a83e0bc --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/destroy.js @@ -0,0 +1,22 @@ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + i.event.unbindAll(); + dom.remove(i.scrollbarX); + dom.remove(i.scrollbarY); + dom.remove(i.scrollbarXRail); + dom.remove(i.scrollbarYRail); + _.removePsClasses(element); + + instances.remove(element); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/click-rail.js b/view/js/perfect-scrollbar/src/js/plugin/handler/click-rail.js new file mode 100644 index 000000000..bbd15218e --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/click-rail.js @@ -0,0 +1,39 @@ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindClickRailHandler(element, i) { + function pageOffset(el) { + return el.getBoundingClientRect(); + } + var stopPropagation = function (e) { e.stopPropagation(); }; + + i.event.bind(i.scrollbarY, 'click', stopPropagation); + i.event.bind(i.scrollbarYRail, 'click', function (e) { + var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top; + var direction = positionTop > i.scrollbarYTop ? 1 : -1; + + updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight); + updateGeometry(element); + + e.stopPropagation(); + }); + + i.event.bind(i.scrollbarX, 'click', stopPropagation); + i.event.bind(i.scrollbarXRail, 'click', function (e) { + var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left; + var direction = positionLeft > i.scrollbarXLeft ? 1 : -1; + + updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth); + updateGeometry(element); + + e.stopPropagation(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindClickRailHandler(element, i); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js b/view/js/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js new file mode 100644 index 000000000..fc99d0083 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js @@ -0,0 +1,103 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseScrollXHandler(element, i) { + var currentLeft = null; + var currentPageX = null; + + function updateScrollLeft(deltaX) { + var newLeft = currentLeft + (deltaX * i.railXRatio); + var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth)); + + if (newLeft < 0) { + i.scrollbarXLeft = 0; + } else if (newLeft > maxLeft) { + i.scrollbarXLeft = maxLeft; + } else { + i.scrollbarXLeft = newLeft; + } + + var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment; + updateScroll(element, 'left', scrollLeft); + } + + var mouseMoveHandler = function (e) { + updateScrollLeft(e.pageX - currentPageX); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'x'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarX, 'mousedown', function (e) { + currentPageX = e.pageX; + currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio; + _.startScrolling(element, 'x'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +function bindMouseScrollYHandler(element, i) { + var currentTop = null; + var currentPageY = null; + + function updateScrollTop(deltaY) { + var newTop = currentTop + (deltaY * i.railYRatio); + var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight)); + + if (newTop < 0) { + i.scrollbarYTop = 0; + } else if (newTop > maxTop) { + i.scrollbarYTop = maxTop; + } else { + i.scrollbarYTop = newTop; + } + + var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight))); + updateScroll(element, 'top', scrollTop); + } + + var mouseMoveHandler = function (e) { + updateScrollTop(e.pageY - currentPageY); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'y'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarY, 'mousedown', function (e) { + currentPageY = e.pageY; + currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio; + _.startScrolling(element, 'y'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseScrollXHandler(element, i); + bindMouseScrollYHandler(element, i); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/keyboard.js b/view/js/perfect-scrollbar/src/js/plugin/handler/keyboard.js new file mode 100644 index 000000000..b23a3bdb1 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/keyboard.js @@ -0,0 +1,154 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindKeyboardHandler(element, i) { + var hovered = false; + i.event.bind(element, 'mouseenter', function () { + hovered = true; + }); + i.event.bind(element, 'mouseleave', function () { + hovered = false; + }); + + var shouldPrevent = false; + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + i.event.bind(i.ownerDocument, 'keydown', function (e) { + if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) { + return; + } + + var focused = dom.matches(i.scrollbarX, ':focus') || + dom.matches(i.scrollbarY, ':focus'); + + if (!hovered && !focused) { + return; + } + + var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement; + if (activeElement) { + if (activeElement.tagName === 'IFRAME') { + activeElement = activeElement.contentDocument.activeElement; + } else { + // go deeper if element is a webcomponent + while (activeElement.shadowRoot) { + activeElement = activeElement.shadowRoot.activeElement; + } + } + if (_.isEditable(activeElement)) { + return; + } + } + + var deltaX = 0; + var deltaY = 0; + + switch (e.which) { + case 37: // left + if (e.metaKey) { + deltaX = -i.contentWidth; + } else if (e.altKey) { + deltaX = -i.containerWidth; + } else { + deltaX = -30; + } + break; + case 38: // up + if (e.metaKey) { + deltaY = i.contentHeight; + } else if (e.altKey) { + deltaY = i.containerHeight; + } else { + deltaY = 30; + } + break; + case 39: // right + if (e.metaKey) { + deltaX = i.contentWidth; + } else if (e.altKey) { + deltaX = i.containerWidth; + } else { + deltaX = 30; + } + break; + case 40: // down + if (e.metaKey) { + deltaY = -i.contentHeight; + } else if (e.altKey) { + deltaY = -i.containerHeight; + } else { + deltaY = -30; + } + break; + case 33: // page up + deltaY = 90; + break; + case 32: // space bar + if (e.shiftKey) { + deltaY = 90; + } else { + deltaY = -90; + } + break; + case 34: // page down + deltaY = -90; + break; + case 35: // end + if (e.ctrlKey) { + deltaY = -i.contentHeight; + } else { + deltaY = -i.containerHeight; + } + break; + case 36: // home + if (e.ctrlKey) { + deltaY = element.scrollTop; + } else { + deltaY = i.containerHeight; + } + break; + default: + return; + } + + updateScroll(element, 'top', element.scrollTop - deltaY); + updateScroll(element, 'left', element.scrollLeft + deltaX); + updateGeometry(element); + + shouldPrevent = shouldPreventDefault(deltaX, deltaY); + if (shouldPrevent) { + e.preventDefault(); + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindKeyboardHandler(element, i); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js b/view/js/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js new file mode 100644 index 000000000..9e08f303b --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js @@ -0,0 +1,141 @@ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseWheelHandler(element, i) { + var shouldPrevent = false; + + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + function getDeltaFromEvent(e) { + var deltaX = e.deltaX; + var deltaY = -1 * e.deltaY; + + if (typeof deltaX === "undefined" || typeof deltaY === "undefined") { + // OS X Safari + deltaX = -1 * e.wheelDeltaX / 6; + deltaY = e.wheelDeltaY / 6; + } + + if (e.deltaMode && e.deltaMode === 1) { + // Firefox in deltaMode 1: Line scrolling + deltaX *= 10; + deltaY *= 10; + } + + if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) { + // IE in some mouse drivers + deltaX = 0; + deltaY = e.wheelDelta; + } + + if (e.shiftKey) { + // reverse axis with shift key + return [-deltaY, -deltaX]; + } + return [deltaX, deltaY]; + } + + function shouldBeConsumedByChild(deltaX, deltaY) { + var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover'); + if (child) { + if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) { + // if not scrollable + return false; + } + + var maxScrollTop = child.scrollHeight - child.clientHeight; + if (maxScrollTop > 0) { + if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) { + return true; + } + } + var maxScrollLeft = child.scrollLeft - child.clientWidth; + if (maxScrollLeft > 0) { + if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) { + return true; + } + } + } + return false; + } + + function mousewheelHandler(e) { + var delta = getDeltaFromEvent(e); + + var deltaX = delta[0]; + var deltaY = delta[1]; + + if (shouldBeConsumedByChild(deltaX, deltaY)) { + return; + } + + shouldPrevent = false; + if (!i.settings.useBothWheelAxes) { + // deltaX will only be used for horizontal scrolling and deltaY will + // only be used for vertical scrolling - this is the default + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else if (i.scrollbarYActive && !i.scrollbarXActive) { + // only vertical scrollbar is active and useBothWheelAxes option is + // active, so let's scroll vertical bar using both mouse wheel axes + if (deltaY) { + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } else if (i.scrollbarXActive && !i.scrollbarYActive) { + // useBothWheelAxes and only horizontal bar is active, so use both + // wheel axes for horizontal bar + if (deltaX) { + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } + + updateGeometry(element); + + shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY)); + if (shouldPrevent) { + e.stopPropagation(); + e.preventDefault(); + } + } + + if (typeof window.onwheel !== "undefined") { + i.event.bind(element, 'wheel', mousewheelHandler); + } else if (typeof window.onmousewheel !== "undefined") { + i.event.bind(element, 'mousewheel', mousewheelHandler); + } +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseWheelHandler(element, i); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/native-scroll.js b/view/js/perfect-scrollbar/src/js/plugin/handler/native-scroll.js new file mode 100644 index 000000000..8664b23a3 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/native-scroll.js @@ -0,0 +1,15 @@ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); + +function bindNativeScrollHandler(element, i) { + i.event.bind(element, 'scroll', function () { + updateGeometry(element); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindNativeScrollHandler(element, i); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/selection.js b/view/js/perfect-scrollbar/src/js/plugin/handler/selection.js new file mode 100644 index 000000000..705420f47 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/selection.js @@ -0,0 +1,115 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindSelectionHandler(element, i) { + function getRangeNode() { + var selection = window.getSelection ? window.getSelection() : + document.getSelection ? document.getSelection() : ''; + if (selection.toString().length === 0) { + return null; + } else { + return selection.getRangeAt(0).commonAncestorContainer; + } + } + + var scrollingLoop = null; + var scrollDiff = {top: 0, left: 0}; + function startScrolling() { + if (!scrollingLoop) { + scrollingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(scrollingLoop); + return; + } + + updateScroll(element, 'top', element.scrollTop + scrollDiff.top); + updateScroll(element, 'left', element.scrollLeft + scrollDiff.left); + updateGeometry(element); + }, 50); // every .1 sec + } + } + function stopScrolling() { + if (scrollingLoop) { + clearInterval(scrollingLoop); + scrollingLoop = null; + } + _.stopScrolling(element); + } + + var isSelected = false; + i.event.bind(i.ownerDocument, 'selectionchange', function () { + if (element.contains(getRangeNode())) { + isSelected = true; + } else { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'mouseup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'keyup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + + i.event.bind(window, 'mousemove', function (e) { + if (isSelected) { + var mousePosition = {x: e.pageX, y: e.pageY}; + var containerGeometry = { + left: element.offsetLeft, + right: element.offsetLeft + element.offsetWidth, + top: element.offsetTop, + bottom: element.offsetTop + element.offsetHeight + }; + + if (mousePosition.x < containerGeometry.left + 3) { + scrollDiff.left = -5; + _.startScrolling(element, 'x'); + } else if (mousePosition.x > containerGeometry.right - 3) { + scrollDiff.left = 5; + _.startScrolling(element, 'x'); + } else { + scrollDiff.left = 0; + } + + if (mousePosition.y < containerGeometry.top + 3) { + if (containerGeometry.top + 3 - mousePosition.y < 5) { + scrollDiff.top = -5; + } else { + scrollDiff.top = -20; + } + _.startScrolling(element, 'y'); + } else if (mousePosition.y > containerGeometry.bottom - 3) { + if (mousePosition.y - containerGeometry.bottom + 3 < 5) { + scrollDiff.top = 5; + } else { + scrollDiff.top = 20; + } + _.startScrolling(element, 'y'); + } else { + scrollDiff.top = 0; + } + + if (scrollDiff.top === 0 && scrollDiff.left === 0) { + stopScrolling(); + } else { + startScrolling(); + } + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindSelectionHandler(element, i); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/handler/touch.js b/view/js/perfect-scrollbar/src/js/plugin/handler/touch.js new file mode 100644 index 000000000..ba5d13307 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/handler/touch.js @@ -0,0 +1,179 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindTouchHandler(element, i, supportsTouch, supportsIePointer) { + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + var scrollLeft = element.scrollLeft; + var magnitudeX = Math.abs(deltaX); + var magnitudeY = Math.abs(deltaY); + + if (magnitudeY > magnitudeX) { + // user is perhaps trying to swipe up/down the page + + if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) || + ((deltaY > 0) && (scrollTop === 0))) { + return !i.settings.swipePropagation; + } + } else if (magnitudeX > magnitudeY) { + // user is perhaps trying to swipe left/right across the page + + if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) || + ((deltaX > 0) && (scrollLeft === 0))) { + return !i.settings.swipePropagation; + } + } + + return true; + } + + function applyTouchMove(differenceX, differenceY) { + updateScroll(element, 'top', element.scrollTop - differenceY); + updateScroll(element, 'left', element.scrollLeft - differenceX); + + updateGeometry(element); + } + + var startOffset = {}; + var startTime = 0; + var speed = {}; + var easingLoop = null; + var inGlobalTouch = false; + var inLocalTouch = false; + + function globalTouchStart() { + inGlobalTouch = true; + } + function globalTouchEnd() { + inGlobalTouch = false; + } + + function getTouch(e) { + if (e.targetTouches) { + return e.targetTouches[0]; + } else { + // Maybe IE pointer + return e; + } + } + function shouldHandle(e) { + if (e.targetTouches && e.targetTouches.length === 1) { + return true; + } + if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + return true; + } + return false; + } + function touchStart(e) { + if (shouldHandle(e)) { + inLocalTouch = true; + + var touch = getTouch(e); + + startOffset.pageX = touch.pageX; + startOffset.pageY = touch.pageY; + + startTime = (new Date()).getTime(); + + if (easingLoop !== null) { + clearInterval(easingLoop); + } + + e.stopPropagation(); + } + } + function touchMove(e) { + if (!inLocalTouch && i.settings.swipePropagation) { + touchStart(e); + } + if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) { + var touch = getTouch(e); + + var currentOffset = {pageX: touch.pageX, pageY: touch.pageY}; + + var differenceX = currentOffset.pageX - startOffset.pageX; + var differenceY = currentOffset.pageY - startOffset.pageY; + + applyTouchMove(differenceX, differenceY); + startOffset = currentOffset; + + var currentTime = (new Date()).getTime(); + + var timeGap = currentTime - startTime; + if (timeGap > 0) { + speed.x = differenceX / timeGap; + speed.y = differenceY / timeGap; + startTime = currentTime; + } + + if (shouldPreventDefault(differenceX, differenceY)) { + e.stopPropagation(); + e.preventDefault(); + } + } + } + function touchEnd() { + if (!inGlobalTouch && inLocalTouch) { + inLocalTouch = false; + + clearInterval(easingLoop); + easingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(easingLoop); + return; + } + + if (!speed.x && !speed.y) { + clearInterval(easingLoop); + return; + } + + if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(easingLoop); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + } + } + + if (supportsTouch) { + i.event.bind(window, 'touchstart', globalTouchStart); + i.event.bind(window, 'touchend', globalTouchEnd); + i.event.bind(element, 'touchstart', touchStart); + i.event.bind(element, 'touchmove', touchMove); + i.event.bind(element, 'touchend', touchEnd); + } else if (supportsIePointer) { + if (window.PointerEvent) { + i.event.bind(window, 'pointerdown', globalTouchStart); + i.event.bind(window, 'pointerup', globalTouchEnd); + i.event.bind(element, 'pointerdown', touchStart); + i.event.bind(element, 'pointermove', touchMove); + i.event.bind(element, 'pointerup', touchEnd); + } else if (window.MSPointerEvent) { + i.event.bind(window, 'MSPointerDown', globalTouchStart); + i.event.bind(window, 'MSPointerUp', globalTouchEnd); + i.event.bind(element, 'MSPointerDown', touchStart); + i.event.bind(element, 'MSPointerMove', touchMove); + i.event.bind(element, 'MSPointerUp', touchEnd); + } + } +} + +module.exports = function (element) { + if (!_.env.supportsTouch && !_.env.supportsIePointer) { + return; + } + + var i = instances.get(element); + bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/initialize.js b/view/js/perfect-scrollbar/src/js/plugin/initialize.js new file mode 100644 index 000000000..05918a5a2 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/initialize.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); + +// Handlers +var handlers = { + 'click-rail': require('./handler/click-rail'), + 'drag-scrollbar': require('./handler/drag-scrollbar'), + 'keyboard': require('./handler/keyboard'), + 'wheel': require('./handler/mouse-wheel'), + 'touch': require('./handler/touch'), + 'selection': require('./handler/selection') +}; +var nativeScrollHandler = require('./handler/native-scroll'); + +module.exports = function (element, userSettings) { + userSettings = typeof userSettings === 'object' ? userSettings : {}; + + cls.add(element, 'ps-container'); + + // Create a plugin instance. + var i = instances.add(element); + + i.settings = _.extend(i.settings, userSettings); + cls.add(element, 'ps-theme-' + i.settings.theme); + + i.settings.handlers.forEach(function (handlerName) { + handlers[handlerName](element); + }); + + nativeScrollHandler(element); + + updateGeometry(element); +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/instances.js b/view/js/perfect-scrollbar/src/js/plugin/instances.js new file mode 100644 index 000000000..d4d74f377 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/instances.js @@ -0,0 +1,107 @@ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var defaultSettings = require('./default-setting'); +var dom = require('../lib/dom'); +var EventManager = require('../lib/event-manager'); +var guid = require('../lib/guid'); + +var instances = {}; + +function Instance(element) { + var i = this; + + i.settings = _.clone(defaultSettings); + i.containerWidth = null; + i.containerHeight = null; + i.contentWidth = null; + i.contentHeight = null; + + i.isRtl = dom.css(element, 'direction') === "rtl"; + i.isNegativeScroll = (function () { + var originalScrollLeft = element.scrollLeft; + var result = null; + element.scrollLeft = -1; + result = element.scrollLeft < 0; + element.scrollLeft = originalScrollLeft; + return result; + })(); + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + i.event = new EventManager(); + i.ownerDocument = element.ownerDocument || document; + + function focus() { + cls.add(element, 'ps-focus'); + } + + function blur() { + cls.remove(element, 'ps-focus'); + } + + i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element); + i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail); + i.scrollbarX.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarX, 'focus', focus); + i.event.bind(i.scrollbarX, 'blur', blur); + i.scrollbarXActive = null; + i.scrollbarXWidth = null; + i.scrollbarXLeft = null; + i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom')); + i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN + i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top')); + i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth')); + // Set rail to display:block to calculate margins + dom.css(i.scrollbarXRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + dom.css(i.scrollbarXRail, 'display', ''); + i.railXWidth = null; + i.railXRatio = null; + + i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element); + i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail); + i.scrollbarY.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarY, 'focus', focus); + i.event.bind(i.scrollbarY, 'blur', blur); + i.scrollbarYActive = null; + i.scrollbarYHeight = null; + i.scrollbarYTop = null; + i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right')); + i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN + i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left')); + i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null; + i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth')); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + dom.css(i.scrollbarYRail, 'display', ''); + i.railYHeight = null; + i.railYRatio = null; +} + +function getId(element) { + return element.getAttribute('data-ps-id'); +} + +function setId(element, id) { + element.setAttribute('data-ps-id', id); +} + +function removeId(element) { + element.removeAttribute('data-ps-id'); +} + +exports.add = function (element) { + var newId = guid(); + setId(element, newId); + instances[newId] = new Instance(element); + return instances[newId]; +}; + +exports.remove = function (element) { + delete instances[getId(element)]; + removeId(element); +}; + +exports.get = function (element) { + return instances[getId(element)]; +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/update-geometry.js b/view/js/perfect-scrollbar/src/js/plugin/update-geometry.js new file mode 100644 index 000000000..b936ea1d3 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/update-geometry.js @@ -0,0 +1,126 @@ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateScroll = require('./update-scroll'); + +function getThumbSize(i, thumbSize) { + if (i.settings.minScrollbarLength) { + thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength); + } + if (i.settings.maxScrollbarLength) { + thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength); + } + return thumbSize; +} + +function updateCss(element, i) { + var xRailOffset = {width: i.railXWidth}; + if (i.isRtl) { + xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth; + } else { + xRailOffset.left = element.scrollLeft; + } + if (i.isScrollbarXUsingBottom) { + xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop; + } else { + xRailOffset.top = i.scrollbarXTop + element.scrollTop; + } + dom.css(i.scrollbarXRail, xRailOffset); + + var yRailOffset = {top: element.scrollTop, height: i.railYHeight}; + if (i.isScrollbarYUsingRight) { + if (i.isRtl) { + yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth; + } else { + yRailOffset.right = i.scrollbarYRight - element.scrollLeft; + } + } else { + if (i.isRtl) { + yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth; + } else { + yRailOffset.left = i.scrollbarYLeft + element.scrollLeft; + } + } + dom.css(i.scrollbarYRail, yRailOffset); + + dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth}); + dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth}); +} + +module.exports = function (element) { + var i = instances.get(element); + + i.containerWidth = element.clientWidth; + i.containerHeight = element.clientHeight; + i.contentWidth = element.scrollWidth; + i.contentHeight = element.scrollHeight; + + var existingRails; + if (!element.contains(i.scrollbarXRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarXRail, element); + } + if (!element.contains(i.scrollbarYRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarYRail, element); + } + + if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) { + i.scrollbarXActive = true; + i.railXWidth = i.containerWidth - i.railXMarginWidth; + i.railXRatio = i.containerWidth / i.railXWidth; + i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth)); + i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth)); + } else { + i.scrollbarXActive = false; + } + + if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) { + i.scrollbarYActive = true; + i.railYHeight = i.containerHeight - i.railYMarginHeight; + i.railYRatio = i.containerHeight / i.railYHeight; + i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight)); + i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight)); + } else { + i.scrollbarYActive = false; + } + + if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) { + i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth; + } + if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) { + i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight; + } + + updateCss(element, i); + + if (i.scrollbarXActive) { + cls.add(element, 'ps-active-x'); + } else { + cls.remove(element, 'ps-active-x'); + i.scrollbarXWidth = 0; + i.scrollbarXLeft = 0; + updateScroll(element, 'left', 0); + } + if (i.scrollbarYActive) { + cls.add(element, 'ps-active-y'); + } else { + cls.remove(element, 'ps-active-y'); + i.scrollbarYHeight = 0; + i.scrollbarYTop = 0; + updateScroll(element, 'top', 0); + } +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/update-scroll.js b/view/js/perfect-scrollbar/src/js/plugin/update-scroll.js new file mode 100644 index 000000000..1a9ad2ccf --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/update-scroll.js @@ -0,0 +1,97 @@ +'use strict'; + +var instances = require('./instances'); + +var lastTop; +var lastLeft; + +var createDOMEvent = function (name) { + var event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; +}; + +module.exports = function (element, axis, value) { + if (typeof element === 'undefined') { + throw 'You must provide an element to the update-scroll function'; + } + + if (typeof axis === 'undefined') { + throw 'You must provide an axis to the update-scroll function'; + } + + if (typeof value === 'undefined') { + throw 'You must provide a value to the update-scroll function'; + } + + if (axis === 'top' && value <= 0) { + element.scrollTop = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-y-reach-start')); + } + + if (axis === 'left' && value <= 0) { + element.scrollLeft = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-x-reach-start')); + } + + var i = instances.get(element); + + if (axis === 'top' && value >= i.contentHeight - i.containerHeight) { + // don't allow scroll past container + value = i.contentHeight - i.containerHeight; + if (value - element.scrollTop <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollTop; + } else { + element.scrollTop = value; + } + element.dispatchEvent(createDOMEvent('ps-y-reach-end')); + } + + if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { + // don't allow scroll past container + value = i.contentWidth - i.containerWidth; + if (value - element.scrollLeft <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollLeft; + } else { + element.scrollLeft = value; + } + element.dispatchEvent(createDOMEvent('ps-x-reach-end')); + } + + if (!lastTop) { + lastTop = element.scrollTop; + } + + if (!lastLeft) { + lastLeft = element.scrollLeft; + } + + if (axis === 'top' && value < lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-up')); + } + + if (axis === 'top' && value > lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-down')); + } + + if (axis === 'left' && value < lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-left')); + } + + if (axis === 'left' && value > lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-right')); + } + + if (axis === 'top') { + element.scrollTop = lastTop = value; + element.dispatchEvent(createDOMEvent('ps-scroll-y')); + } + + if (axis === 'left') { + element.scrollLeft = lastLeft = value; + element.dispatchEvent(createDOMEvent('ps-scroll-x')); + } + +}; diff --git a/view/js/perfect-scrollbar/src/js/plugin/update.js b/view/js/perfect-scrollbar/src/js/plugin/update.js new file mode 100644 index 000000000..cc9da15f4 --- /dev/null +++ b/view/js/perfect-scrollbar/src/js/plugin/update.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); +var updateScroll = require('./update-scroll'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + // Recalcuate negative scrollLeft adjustment + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + + // Recalculate rail margins + dom.css(i.scrollbarXRail, 'display', 'block'); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + + // Hide scrollbars not to affect scrollWidth and scrollHeight + dom.css(i.scrollbarXRail, 'display', 'none'); + dom.css(i.scrollbarYRail, 'display', 'none'); + + updateGeometry(element); + + // Update top/left scroll to trigger events + updateScroll(element, 'top', element.scrollTop); + updateScroll(element, 'left', element.scrollLeft); + + dom.css(i.scrollbarXRail, 'display', ''); + dom.css(i.scrollbarYRail, 'display', ''); +}; diff --git a/view/templates/head.tpl b/view/templates/head.tpl index 3896a4b4a..a83f3dd20 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -6,7 +6,7 @@ - + {{foreach $stylesheets as $stylesheetUrl}} @@ -40,7 +40,7 @@ - + diff --git a/view/theme/frio/templates/head.tpl b/view/theme/frio/templates/head.tpl index b539a90d9..ae00ea85e 100644 --- a/view/theme/frio/templates/head.tpl +++ b/view/theme/frio/templates/head.tpl @@ -11,7 +11,7 @@ - + @@ -61,7 +61,7 @@ - +